From 5230224409707cbb1bf590d8c78427deb2f43f9e Mon Sep 17 00:00:00 2001 From: guilhermer Date: Tue, 12 Mar 2024 15:05:44 +0100 Subject: [PATCH 1/5] feat: adding flag isExpress --- .../src/components/ApplePay/ApplePay.test.ts | 11 +++ .../lib/src/components/ApplePay/ApplePay.tsx | 10 ++- .../src/components/ApplePay/defaultProps.ts | 12 +-- .../ApplePay/payment-request.test.ts | 1 + packages/lib/src/components/ApplePay/types.ts | 16 ++-- .../components/GooglePay/GooglePay.test.ts | 11 +++ .../src/components/GooglePay/GooglePay.tsx | 8 +- .../src/components/GooglePay/defaultProps.ts | 10 ++- .../lib/src/components/GooglePay/requests.ts | 5 +- .../lib/src/components/GooglePay/types.ts | 74 ++++++++++--------- 10 files changed, 96 insertions(+), 62 deletions(-) diff --git a/packages/lib/src/components/ApplePay/ApplePay.test.ts b/packages/lib/src/components/ApplePay/ApplePay.test.ts index 66bc7298fb..c9e7768fcc 100644 --- a/packages/lib/src/components/ApplePay/ApplePay.test.ts +++ b/packages/lib/src/components/ApplePay/ApplePay.test.ts @@ -19,6 +19,17 @@ beforeEach(() => { }); describe('ApplePay', () => { + describe('isExpress flag', () => { + test('should add subtype: express when isExpress is configured', () => { + const applepay = new ApplePay(global.core, { isExpress: true }); + expect(applepay.data.paymentMethod).toHaveProperty('subtype', 'express'); + }); + test('should not add subtype: express when isExpress is omitted', () => { + const applepay = new ApplePay(global.core); + expect(applepay.data.paymentMethod).not.toHaveProperty('subtype', 'express'); + }); + }); + describe('submit()', () => { test('should forward apple pay error (if available) to ApplePay if payment fails', async () => { const onPaymentFailedMock = jest.fn(); diff --git a/packages/lib/src/components/ApplePay/ApplePay.tsx b/packages/lib/src/components/ApplePay/ApplePay.tsx index 495cb49ec5..ad13bf337f 100644 --- a/packages/lib/src/components/ApplePay/ApplePay.tsx +++ b/packages/lib/src/components/ApplePay/ApplePay.tsx @@ -34,7 +34,7 @@ class ApplePayElement extends UIElement { /** * Formats the component props */ - protected formatProps(props) { + protected override formatProps(props) { const version = props.version || resolveSupportedVersion(latestSupportedVersion); const supportedNetworks = props.brands?.length ? mapBrands(props.brands) : props.supportedNetworks; @@ -50,20 +50,22 @@ class ApplePayElement extends UIElement { /** * Formats the component data output */ - protected formatData(): ApplePayElementData { + protected override formatData(): ApplePayElementData { const { applePayToken, billingAddress, deliveryAddress } = this.state; + const { isExpress } = this.props; return { paymentMethod: { type: ApplePayElement.type, - applePayToken + applePayToken, + ...(isExpress && { subtype: 'express' }) }, ...(billingAddress && { billingAddress }), ...(deliveryAddress && { deliveryAddress }) }; } - public submit = (): void => { + public override submit = (): void => { // Analytics if (this.props.isInstantPayment) { this.submitAnalytics({ type: ANALYTICS_SELECTED_STR, target: ANALYTICS_INSTANT_PAYMENT_BUTTON }); diff --git a/packages/lib/src/components/ApplePay/defaultProps.ts b/packages/lib/src/components/ApplePay/defaultProps.ts index 4411073d07..dc666400e8 100644 --- a/packages/lib/src/components/ApplePay/defaultProps.ts +++ b/packages/lib/src/components/ApplePay/defaultProps.ts @@ -1,12 +1,9 @@ -const defaultProps = { +import { ApplePayConfiguration } from './types'; + +const defaultProps: ApplePayConfiguration = { // Transaction Information amount: { currency: 'USD', value: 0 }, - /** - * The merchant’s two-letter ISO 3166 country code. - */ - countryCode: 'US', - totalPriceStatus: 'final', totalPriceLabel: undefined, @@ -82,8 +79,7 @@ const defaultProps = { // ButtonOptions buttonType: 'plain', - buttonColor: 'black', - showPayButton: true // show or hide the Apple Pay button + buttonColor: 'black' }; export default defaultProps; diff --git a/packages/lib/src/components/ApplePay/payment-request.test.ts b/packages/lib/src/components/ApplePay/payment-request.test.ts index 0d83985a53..32d6b7ae9d 100644 --- a/packages/lib/src/components/ApplePay/payment-request.test.ts +++ b/packages/lib/src/components/ApplePay/payment-request.test.ts @@ -47,6 +47,7 @@ describe('preparePaymentRequest', () => { test('works with defaultProps', () => { const paymentRequest = preparePaymentRequest({ ...defaultProps, + countryCode: 'US', companyName: 'Test' }); diff --git a/packages/lib/src/components/ApplePay/types.ts b/packages/lib/src/components/ApplePay/types.ts index 5e865e5b56..eb1ca1bc34 100644 --- a/packages/lib/src/components/ApplePay/types.ts +++ b/packages/lib/src/components/ApplePay/types.ts @@ -38,6 +38,12 @@ export type ApplePayButtonType = | 'top-up'; export interface ApplePayConfiguration extends UIElementProps { + /** + * Flag used to enable Express Flow reports on Customer Area + * @defaultValue false + */ + isExpress?: boolean; + /** * The Apple Pay version number your website supports. * @default highest supported version by the shopper device @@ -203,20 +209,16 @@ export interface ApplePayConfiguration extends UIElementProps { // ButtonOptions buttonColor?: 'black' | 'white' | 'white-with-line'; buttonType?: ApplePayButtonType; - - /** - * Show or hide the Apple Pay button - */ - showPayButton?: boolean; } export interface ApplePayElementData { paymentMethod: { type: string; applePayToken: string; - billingAddress?: AddressData; - deliveryAddress?: AddressData; + isExpress?: boolean; }; + billingAddress?: AddressData; + deliveryAddress?: AddressData; } export interface ApplePaySessionRequest { diff --git a/packages/lib/src/components/GooglePay/GooglePay.test.ts b/packages/lib/src/components/GooglePay/GooglePay.test.ts index 8e9f11698b..5b4e31b0d0 100644 --- a/packages/lib/src/components/GooglePay/GooglePay.test.ts +++ b/packages/lib/src/components/GooglePay/GooglePay.test.ts @@ -55,6 +55,17 @@ beforeEach(() => { }); describe('GooglePay', () => { + describe('isExpress flag', () => { + test('should add subtype: express when isExpress is configured', () => { + const googlepay = new GooglePay(global.core, { isExpress: true }); + expect(googlepay.data.paymentMethod).toHaveProperty('subtype', 'express'); + }); + test('should not add subtype: express when isExpress is omitted', () => { + const googlepay = new GooglePay(global.core); + expect(googlepay.data.paymentMethod).not.toHaveProperty('subtype', 'express'); + }); + }); + describe('submit()', () => { test('should make the payments call passing deliveryAddress and billingAddress', async () => { const onSubmitMock = jest.fn().mockImplementation((data, component, actions) => { diff --git a/packages/lib/src/components/GooglePay/GooglePay.tsx b/packages/lib/src/components/GooglePay/GooglePay.tsx index 60ff166138..3f77de3ded 100644 --- a/packages/lib/src/components/GooglePay/GooglePay.tsx +++ b/packages/lib/src/components/GooglePay/GooglePay.tsx @@ -33,7 +33,7 @@ class GooglePay extends UIElement { }); } - formatProps(props): GooglePayConfiguration { + protected override formatProps(props): GooglePayConfiguration { const buttonSizeMode = props.buttonSizeMode ?? (props.isDropin ? 'fill' : 'static'); const buttonLocale = getGooglePayLocale(props.buttonLocale ?? props.i18n?.locale); @@ -51,14 +51,16 @@ class GooglePay extends UIElement { /** * Formats the component data output */ - formatData() { + protected override formatData() { const { googlePayCardNetwork, googlePayToken, billingAddress, deliveryAddress } = this.state; + const { isExpress } = this.props; return { paymentMethod: { type: this.type, googlePayCardNetwork, - googlePayToken + googlePayToken, + ...(isExpress && { subtype: 'express' }) }, browserInfo: this.browserInfo, origin: !!window && window.location.origin, diff --git a/packages/lib/src/components/GooglePay/defaultProps.ts b/packages/lib/src/components/GooglePay/defaultProps.ts index c125a1dc75..f149b15744 100644 --- a/packages/lib/src/components/GooglePay/defaultProps.ts +++ b/packages/lib/src/components/GooglePay/defaultProps.ts @@ -1,5 +1,7 @@ -export default { - environment: 'TEST', +import { GooglePayConfiguration } from './types'; + +const defaultProps: GooglePayConfiguration = { + isExpress: false, // isReadyToPayRequest existingPaymentMethodRequired: false, @@ -46,7 +48,7 @@ export default { shippingAddressParameters: undefined, // https://developers.google.com/pay/api/web/reference/object#ShippingAddressParameters shippingOptionRequired: false, shippingOptionParameters: undefined, - paymentMethods: [], - callbackIntents: [] }; + +export default defaultProps; diff --git a/packages/lib/src/components/GooglePay/requests.ts b/packages/lib/src/components/GooglePay/requests.ts index 4673127933..7a320d0af3 100644 --- a/packages/lib/src/components/GooglePay/requests.ts +++ b/packages/lib/src/components/GooglePay/requests.ts @@ -12,7 +12,10 @@ export function isReadyToPayRequest({ allowedAuthMethods, allowedCardNetworks, existingPaymentMethodRequired = false -}): google.payments.api.IsReadyToPayRequest { +}: Pick< + GooglePayConfiguration, + 'allowedAuthMethods' | 'allowedCardNetworks' | 'existingPaymentMethodRequired' +>): google.payments.api.IsReadyToPayRequest { return { apiVersion: config.API_VERSION, apiVersionMinor: config.API_VERSION_MINOR, diff --git a/packages/lib/src/components/GooglePay/types.ts b/packages/lib/src/components/GooglePay/types.ts index 23043da12c..442076abb4 100644 --- a/packages/lib/src/components/GooglePay/types.ts +++ b/packages/lib/src/components/GooglePay/types.ts @@ -1,42 +1,14 @@ -import { AddressData } from '../../types'; -import { UIElementProps } from '../internal/UIElement/types'; - -export interface GooglePayPropsConfiguration { - /** - * Adyen's merchant account name - * @see https://developers.google.com/pay/api/web/reference/request-objects#gateway - */ - gatewayMerchantId: string; - - /** - * A Google merchant identifier issued after registration with the {@link https://pay.google.com/business/console | Google Pay Business Console}. - * Required when PaymentsClient is initialized with an environment property of PRODUCTION. - * @see https://developers.google.com/pay/api/web/reference/request-objects#MerchantInfo - */ - merchantId?: string; - - /** - * Merchant name is rendered in the payment sheet. - * @see https://developers.google.com/pay/api/web/reference/request-objects#MerchantInfo - */ - merchantName?: string; - - /** - * Merchant fully qualified domain name. - */ - merchantOrigin?: string; - - /** - * Google JWT solution for platforms - * To request Google Pay credentials, you can enable platforms to send requests that are authenticated with the platform credentials. You don't need to register individual domain names to call Google Pay APIs. - */ - authJwt?: string; -} +import type { AddressData } from '../../types'; +import type { UIElementProps } from '../internal/UIElement/types'; export interface GooglePayConfiguration extends UIElementProps { type?: 'googlepay' | 'paywithgoogle'; - configuration?: GooglePayPropsConfiguration; + /** + * Flag used to enable Express Flow reports on Customer Area + * @defaultValue false + */ + isExpress?: boolean; /** * @see https://developers.google.com/pay/api/web/reference/request-objects#IsReadyToPayRequest @@ -163,6 +135,38 @@ export interface GooglePayConfiguration extends UIElementProps { }, actions: { resolve: () => void; reject: (error?: google.payments.api.PaymentDataError | string) => void } ) => void; + + configuration?: { + /** + * Adyen's merchant account name + * @see https://developers.google.com/pay/api/web/reference/request-objects#gateway + */ + gatewayMerchantId: string; + + /** + * A Google merchant identifier issued after registration with the {@link https://pay.google.com/business/console | Google Pay Business Console}. + * Required when PaymentsClient is initialized with an environment property of PRODUCTION. + * @see https://developers.google.com/pay/api/web/reference/request-objects#MerchantInfo + */ + merchantId?: string; + + /** + * Merchant name is rendered in the payment sheet. + * @see https://developers.google.com/pay/api/web/reference/request-objects#MerchantInfo + */ + merchantName?: string; + + /** + * Merchant fully qualified domain name. + */ + merchantOrigin?: string; + + /** + * Google JWT solution for platforms + * To request Google Pay credentials, you can enable platforms to send requests that are authenticated with the platform credentials. You don't need to register individual domain names to call Google Pay APIs. + */ + authJwt?: string; + }; } // Used to add undocumented google payment options From cde424009a6cc9fa4dcd9cd478d4badcd1fefd11 Mon Sep 17 00:00:00 2001 From: guilhermer Date: Wed, 13 Mar 2024 13:05:54 +0100 Subject: [PATCH 2/5] using isexpress flag on googlepay --- .../components/GooglePay/GooglePay.test.ts | 16 ++++++++-------- .../src/components/GooglePay/GooglePay.tsx | 19 +++++++++++++------ .../components/GooglePay/GooglePayService.ts | 11 ++++------- .../lib/src/components/GooglePay/types.ts | 7 +++++-- packages/lib/src/core/core.ts | 5 +++-- .../wallets/GooglePayExpress.stories.tsx | 2 ++ 6 files changed, 35 insertions(+), 25 deletions(-) diff --git a/packages/lib/src/components/GooglePay/GooglePay.test.ts b/packages/lib/src/components/GooglePay/GooglePay.test.ts index 5b4e31b0d0..af762624e9 100644 --- a/packages/lib/src/components/GooglePay/GooglePay.test.ts +++ b/packages/lib/src/components/GooglePay/GooglePay.test.ts @@ -81,7 +81,7 @@ describe('GooglePay', () => { }); // @ts-ignore GooglePayService is mocked - const onPaymentAuthorized = GooglePayService.mock.calls[0][0].paymentDataCallbacks.onPaymentAuthorized; + const onPaymentAuthorized = GooglePayService.mock.calls[0][1].onPaymentAuthorized; const promise = onPaymentAuthorized(googlePaymentData); await new Promise(process.nextTick); @@ -144,7 +144,7 @@ describe('GooglePay', () => { }); // @ts-ignore GooglePayService is mocked - const onPaymentAuthorized = GooglePayService.mock.calls[0][0].paymentDataCallbacks.onPaymentAuthorized; + const onPaymentAuthorized = GooglePayService.mock.calls[0][1].onPaymentAuthorized; const googlePaymentDataWithoutAddresses = { ...googlePaymentData }; delete googlePaymentDataWithoutAddresses.shippingAddress; delete googlePaymentDataWithoutAddresses.paymentMethodData.info.billingAddress; @@ -184,7 +184,7 @@ describe('GooglePay', () => { }); // @ts-ignore GooglePayService is mocked - const onPaymentAuthorized = GooglePayService.mock.calls[0][0].paymentDataCallbacks.onPaymentAuthorized; + const onPaymentAuthorized = GooglePayService.mock.calls[0][1].onPaymentAuthorized; const promise = onPaymentAuthorized(googlePaymentData); await new Promise(process.nextTick); @@ -226,7 +226,7 @@ describe('GooglePay', () => { }); // @ts-ignore GooglePayService is mocked - const onPaymentAuthorized = GooglePayService.mock.calls[0][0].paymentDataCallbacks.onPaymentAuthorized; + const onPaymentAuthorized = GooglePayService.mock.calls[0][1].onPaymentAuthorized; const promise = onPaymentAuthorized(googlePaymentData); await new Promise(process.nextTick); @@ -305,7 +305,7 @@ describe('GooglePay', () => { new GooglePay(global.core, { onAuthorized: onAuthorizedMock }); // @ts-ignore GooglePayService is mocked - const onPaymentAuthorized = GooglePayService.mock.calls[0][0].paymentDataCallbacks.onPaymentAuthorized; + const onPaymentAuthorized = GooglePayService.mock.calls[0][1].onPaymentAuthorized; onPaymentAuthorized(googlePaymentData); expect(onAuthorizedMock.mock.calls[0][0]).toStrictEqual(event); @@ -324,7 +324,7 @@ describe('GooglePay', () => { }); // @ts-ignore GooglePayService is mocked - const onPaymentAuthorized = GooglePayService.mock.calls[0][0].paymentDataCallbacks.onPaymentAuthorized; + const onPaymentAuthorized = GooglePayService.mock.calls[0][1].onPaymentAuthorized; const promise = onPaymentAuthorized(googlePaymentData); expect(promise).resolves.toEqual({ @@ -355,7 +355,7 @@ describe('GooglePay', () => { const paymentCall = jest.spyOn(gpay as any, 'makePaymentsCall'); // @ts-ignore GooglePayService is mocked - const onPaymentAuthorized = GooglePayService.mock.calls[0][0].paymentDataCallbacks.onPaymentAuthorized; + const onPaymentAuthorized = GooglePayService.mock.calls[0][1].onPaymentAuthorized; onPaymentAuthorized(googlePaymentData); await new Promise(process.nextTick); @@ -368,7 +368,7 @@ describe('GooglePay', () => { const paymentCall = jest.spyOn(gpay as any, 'makePaymentsCall'); // @ts-ignore GooglePayService is mocked - const onPaymentAuthorized = GooglePayService.mock.calls[0][0].paymentDataCallbacks.onPaymentAuthorized; + const onPaymentAuthorized = GooglePayService.mock.calls[0][1].onPaymentAuthorized; onPaymentAuthorized(googlePaymentData); await new Promise(process.nextTick); diff --git a/packages/lib/src/components/GooglePay/GooglePay.tsx b/packages/lib/src/components/GooglePay/GooglePay.tsx index 3f77de3ded..a7e42e85a8 100644 --- a/packages/lib/src/components/GooglePay/GooglePay.tsx +++ b/packages/lib/src/components/GooglePay/GooglePay.tsx @@ -9,6 +9,7 @@ import AdyenCheckoutError from '../../core/Errors/AdyenCheckoutError'; import { TxVariants } from '../tx-variants'; import { sanitizeResponse, verifyPaymentDidNotFail } from '../internal/UIElement/utils'; import { ANALYTICS_INSTANT_PAYMENT_BUTTON, ANALYTICS_SELECTED_STR } from '../../core/Analytics/constants'; + import type { AddressData, PaymentResponseData, RawPaymentResponse } from '../../types/global-types'; import type { GooglePayConfiguration } from './types'; import type { ICore } from '../../core/types'; @@ -24,12 +25,18 @@ class GooglePay extends UIElement { super(checkout, props); this.handleAuthorization = this.handleAuthorization.bind(this); - this.googlePay = new GooglePayService({ - ...this.props, - paymentDataCallbacks: { - ...this.props.paymentDataCallbacks, - onPaymentAuthorized: this.onPaymentAuthorized - } + const { isExpress, paymentDataCallbacks } = this.props; + + if (paymentDataCallbacks?.onPaymentDataChanged && isExpress === false) { + throw new AdyenCheckoutError( + 'IMPLEMENTATION_ERROR', + 'GooglePay - You must set "isExpress" flag to "true" in order to use "onPaymentDataChanged" callback' + ); + } + + this.googlePay = new GooglePayService(this.props.environment, { + ...(isExpress && paymentDataCallbacks?.onPaymentDataChanged && { onPaymentDataChanged: paymentDataCallbacks.onPaymentDataChanged }), + onPaymentAuthorized: this.onPaymentAuthorized }); } diff --git a/packages/lib/src/components/GooglePay/GooglePayService.ts b/packages/lib/src/components/GooglePay/GooglePayService.ts index bd2d1626bb..a2fbd28d37 100644 --- a/packages/lib/src/components/GooglePay/GooglePayService.ts +++ b/packages/lib/src/components/GooglePay/GooglePayService.ts @@ -7,15 +7,12 @@ import type { GooglePayConfiguration } from './types'; class GooglePayService { public readonly paymentsClient: Promise; - constructor(props: GooglePayConfiguration) { - const environment = resolveEnvironment(props.environment); - if (environment === 'TEST' && process.env.NODE_ENV === 'development') { - console.warn('Google Pay initiated in TEST mode. Request non-chargeable payment methods suitable for testing.'); - } + constructor(environment: string, paymentDataCallbacks: google.payments.api.PaymentDataCallbacks) { + const googlePayEnvironment = resolveEnvironment(environment); this.paymentsClient = this.getGooglePaymentsClient({ - environment, - paymentDataCallbacks: props.paymentDataCallbacks + environment: googlePayEnvironment, + paymentDataCallbacks }); } diff --git a/packages/lib/src/components/GooglePay/types.ts b/packages/lib/src/components/GooglePay/types.ts index 442076abb4..4f74ca0bbb 100644 --- a/packages/lib/src/components/GooglePay/types.ts +++ b/packages/lib/src/components/GooglePay/types.ts @@ -5,7 +5,7 @@ export interface GooglePayConfiguration extends UIElementProps { type?: 'googlepay' | 'paywithgoogle'; /** - * Flag used to enable Express Flow reports on Customer Area + * Enables the GooglePay Express Flow * @defaultValue false */ isExpress?: boolean; @@ -104,9 +104,12 @@ export interface GooglePayConfiguration extends UIElementProps { callbackIntents?: google.payments.api.CallbackIntent[]; /** + * Disclaimer: 'onPaymentAuthorized' is not exposed as we are using our own method internally to + * handle the authorization part + * * @see https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataCallbacks */ - paymentDataCallbacks?: google.payments.api.PaymentDataCallbacks; + paymentDataCallbacks?: Pick; /** * @see https://developers.google.com/pay/api/web/reference/request-objects#TransactionInfo diff --git a/packages/lib/src/core/core.ts b/packages/lib/src/core/core.ts index 420679b76d..3777a01577 100644 --- a/packages/lib/src/core/core.ts +++ b/packages/lib/src/core/core.ts @@ -5,8 +5,6 @@ import PaymentMethods from './ProcessResponse/PaymentMethods'; import getComponentForAction from './ProcessResponse/PaymentAction'; import { resolveEnvironment, resolveCDNEnvironment, resolveAnalyticsEnvironment } from './Environment'; import Analytics from './Analytics'; -import { AdditionalDetailsStateData, PaymentAction, PaymentResponseData } from '../types/global-types'; -import { CoreConfiguration, ICore } from './types'; import { processGlobalOptions } from './utils'; import Session from './CheckoutSession'; import { hasOwnProperty } from '../utils/hasOwnProperty'; @@ -19,6 +17,9 @@ import { ANALYTICS_ACTION_STR } from './Analytics/constants'; import { THREEDS2_FULL } from '../components/ThreeDS2/config'; import { DEFAULT_LOCALE } from '../language/config'; +import type { AdditionalDetailsStateData, PaymentAction, PaymentResponseData } from '../types/global-types'; +import type { CoreConfiguration, ICore } from './types'; + class Core implements ICore { public session?: Session; public paymentMethodsResponse: PaymentMethods; diff --git a/packages/lib/storybook/stories/wallets/GooglePayExpress.stories.tsx b/packages/lib/storybook/stories/wallets/GooglePayExpress.stories.tsx index d1b0d6a6db..1593697f58 100644 --- a/packages/lib/storybook/stories/wallets/GooglePayExpress.stories.tsx +++ b/packages/lib/storybook/stories/wallets/GooglePayExpress.stories.tsx @@ -171,6 +171,8 @@ export const Express: GooglePayStory = { amount: INITIAL_AMOUNT, shopperLocale: SHOPPER_LOCALE, componentConfiguration: { + isExpress: true, + onSubmit: async (state, component, actions) => { try { const paymentData = { From 6b929e20770ac365913949f89ee80653de4ce07c Mon Sep 17 00:00:00 2001 From: guilhermer Date: Wed, 13 Mar 2024 13:32:07 +0100 Subject: [PATCH 3/5] adding check for applepay --- .../src/components/ApplePay/ApplePay.test.ts | 4 + .../lib/src/components/ApplePay/ApplePay.tsx | 11 +++ .../src/components/ApplePay/defaultProps.ts | 76 +------------------ packages/lib/src/components/ApplePay/types.ts | 2 +- .../components/GooglePay/GooglePay.test.ts | 4 + .../src/components/GooglePay/GooglePay.tsx | 2 +- 6 files changed, 24 insertions(+), 75 deletions(-) diff --git a/packages/lib/src/components/ApplePay/ApplePay.test.ts b/packages/lib/src/components/ApplePay/ApplePay.test.ts index c9e7768fcc..37607dc922 100644 --- a/packages/lib/src/components/ApplePay/ApplePay.test.ts +++ b/packages/lib/src/components/ApplePay/ApplePay.test.ts @@ -28,6 +28,10 @@ describe('ApplePay', () => { const applepay = new ApplePay(global.core); expect(applepay.data.paymentMethod).not.toHaveProperty('subtype', 'express'); }); + + test('should throw error when express callbacks are passed but isExpress flag is not set', () => { + expect(() => new ApplePay(global.core, { onShippingContactSelected: jest.fn() })).toThrow(); + }); }); describe('submit()', () => { diff --git a/packages/lib/src/components/ApplePay/ApplePay.tsx b/packages/lib/src/components/ApplePay/ApplePay.tsx index ad13bf337f..eaac32d68c 100644 --- a/packages/lib/src/components/ApplePay/ApplePay.tsx +++ b/packages/lib/src/components/ApplePay/ApplePay.tsx @@ -12,6 +12,7 @@ import AdyenCheckoutError from '../../core/Errors/AdyenCheckoutError'; import { TxVariants } from '../tx-variants'; import { sanitizeResponse, verifyPaymentDidNotFail } from '../internal/UIElement/utils'; import { ANALYTICS_INSTANT_PAYMENT_BUTTON, ANALYTICS_SELECTED_STR } from '../../core/Analytics/constants'; + import type { ApplePayConfiguration, ApplePayElementData, ApplePayPaymentOrderDetails, ApplePaySessionRequest } from './types'; import type { ICore } from '../../core/types'; import type { PaymentResponseData, RawPaymentResponse } from '../../types/global-types'; @@ -24,6 +25,16 @@ class ApplePayElement extends UIElement { constructor(checkout: ICore, props?: ApplePayConfiguration) { super(checkout, props); + + const { isExpress, onShippingContactSelected, onShippingMethodSelected } = this.props; + + if (isExpress === false && (onShippingContactSelected || onShippingMethodSelected)) { + throw new AdyenCheckoutError( + 'IMPLEMENTATION_ERROR', + 'ApplePay - You must set "isExpress" flag to "true" in order to use "onShippingContactSelected" and/or "onShippingMethodSelected" callback' + ); + } + this.startSession = this.startSession.bind(this); this.submit = this.submit.bind(this); this.validateMerchant = this.validateMerchant.bind(this); diff --git a/packages/lib/src/components/ApplePay/defaultProps.ts b/packages/lib/src/components/ApplePay/defaultProps.ts index dc666400e8..bff389c217 100644 --- a/packages/lib/src/components/ApplePay/defaultProps.ts +++ b/packages/lib/src/components/ApplePay/defaultProps.ts @@ -1,85 +1,15 @@ import { ApplePayConfiguration } from './types'; const defaultProps: ApplePayConfiguration = { - // Transaction Information + isExpress: false, amount: { currency: 'USD', value: 0 }, - totalPriceStatus: 'final', - totalPriceLabel: undefined, - - configuration: { - merchantName: '', - merchantId: '' - }, - initiative: 'web', - - /** - * https://developer.apple.com/documentation/apple_pay_on_the_web/applepaypaymentrequest/1916120-lineitems - * A set of line items that explain recurring payments and additional charges and discounts. - */ - lineItems: undefined, - - /** - * https://developer.apple.com/documentation/apple_pay_on_the_web/applepaypaymentrequest/1916123-merchantcapabilities - * The payment capabilities supported by the merchant. - */ merchantCapabilities: ['supports3DS'], - - /** - * https://developer.apple.com/documentation/apple_pay_on_the_web/applepaypaymentrequest/1916121-shippingmethods - * A list of available methods for shipping physical goods. - */ - shippingMethods: undefined, - - /** - * https://developer.apple.com/documentation/apple_pay_on_the_web/applepaypaymentrequest/1916128-shippingtype - * An optional value that indicates how purchased items are to be shipped. - */ - shippingType: undefined, - - /** - * https://developer.apple.com/documentation/apple_pay_on_the_web/applepaypaymentrequest/2928612-supportedcountries - * A list of two-character country codes you provide, used to limit payments to cards from specific countries. - */ - supportedCountries: undefined, - - /** - * https://developer.apple.com/documentation/apple_pay_on_the_web/applepaypaymentrequest/1916122-supportednetworks - * The payment networks supported by the merchant. - */ supportedNetworks: ['amex', 'discover', 'masterCard', 'visa'], - - // Requested Billing and Shipping Contact Information - - /** - * The fields of billing information that you require from the user to process the transaction. - */ - requiredBillingContactFields: undefined, - - /** - * The fields of shipping information that you require from the user to fulfill the order. - */ - requiredShippingContactFields: undefined, - - // Known Contact Information - - billingContact: undefined, // Billing contact information for the user. - shippingContact: undefined, // Shipping contact information for the user. - - // Custom Data - - applicationData: undefined, // A Base64-encoded string used to contain your application-specific data. - - // Events - onClick: resolve => resolve(), - onPaymentMethodSelected: null, - onShippingContactSelected: null, - onShippingMethodSelected: null, - - // ButtonOptions buttonType: 'plain', - buttonColor: 'black' + buttonColor: 'black', + onClick: resolve => resolve() }; export default defaultProps; diff --git a/packages/lib/src/components/ApplePay/types.ts b/packages/lib/src/components/ApplePay/types.ts index eb1ca1bc34..b46a4dcdd4 100644 --- a/packages/lib/src/components/ApplePay/types.ts +++ b/packages/lib/src/components/ApplePay/types.ts @@ -39,7 +39,7 @@ export type ApplePayButtonType = export interface ApplePayConfiguration extends UIElementProps { /** - * Flag used to enable Express Flow reports on Customer Area + * Enables the ApplePay Express Flow * @defaultValue false */ isExpress?: boolean; diff --git a/packages/lib/src/components/GooglePay/GooglePay.test.ts b/packages/lib/src/components/GooglePay/GooglePay.test.ts index af762624e9..e2f580030b 100644 --- a/packages/lib/src/components/GooglePay/GooglePay.test.ts +++ b/packages/lib/src/components/GooglePay/GooglePay.test.ts @@ -64,6 +64,10 @@ describe('GooglePay', () => { const googlepay = new GooglePay(global.core); expect(googlepay.data.paymentMethod).not.toHaveProperty('subtype', 'express'); }); + + test('should throw error when express callbacks are passed but isExpress flag is not set', () => { + expect(() => new GooglePay(global.core, { paymentDataCallbacks: { onPaymentDataChanged: jest.fn() } })).toThrow(); + }); }); describe('submit()', () => { diff --git a/packages/lib/src/components/GooglePay/GooglePay.tsx b/packages/lib/src/components/GooglePay/GooglePay.tsx index a7e42e85a8..40d26c7e8c 100644 --- a/packages/lib/src/components/GooglePay/GooglePay.tsx +++ b/packages/lib/src/components/GooglePay/GooglePay.tsx @@ -27,7 +27,7 @@ class GooglePay extends UIElement { const { isExpress, paymentDataCallbacks } = this.props; - if (paymentDataCallbacks?.onPaymentDataChanged && isExpress === false) { + if (isExpress === false && paymentDataCallbacks?.onPaymentDataChanged) { throw new AdyenCheckoutError( 'IMPLEMENTATION_ERROR', 'GooglePay - You must set "isExpress" flag to "true" in order to use "onPaymentDataChanged" callback' From 7ace29a4098273cf019f7e4864cb93237f457270 Mon Sep 17 00:00:00 2001 From: guilhermer Date: Wed, 13 Mar 2024 13:37:25 +0100 Subject: [PATCH 4/5] fixed playground --- packages/lib/src/components/ApplePay/ApplePay.tsx | 2 +- .../lib/storybook/stories/wallets/ApplePayExpress.stories.tsx | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/lib/src/components/ApplePay/ApplePay.tsx b/packages/lib/src/components/ApplePay/ApplePay.tsx index eaac32d68c..1663109d38 100644 --- a/packages/lib/src/components/ApplePay/ApplePay.tsx +++ b/packages/lib/src/components/ApplePay/ApplePay.tsx @@ -31,7 +31,7 @@ class ApplePayElement extends UIElement { if (isExpress === false && (onShippingContactSelected || onShippingMethodSelected)) { throw new AdyenCheckoutError( 'IMPLEMENTATION_ERROR', - 'ApplePay - You must set "isExpress" flag to "true" in order to use "onShippingContactSelected" and/or "onShippingMethodSelected" callback' + 'ApplePay - You must set "isExpress" flag to "true" in order to use "onShippingContactSelected" and/or "onShippingMethodSelected" callbacks' ); } diff --git a/packages/lib/storybook/stories/wallets/ApplePayExpress.stories.tsx b/packages/lib/storybook/stories/wallets/ApplePayExpress.stories.tsx index ba63bffcfe..de4cd84f50 100644 --- a/packages/lib/storybook/stories/wallets/ApplePayExpress.stories.tsx +++ b/packages/lib/storybook/stories/wallets/ApplePayExpress.stories.tsx @@ -152,6 +152,8 @@ export const Express: ApplePayStory = { amount: INITIAL_AMOUNT, shopperLocale: SHOPPER_LOCALE, componentConfiguration: { + isExpress: true, + onSubmit: async (state, component, actions) => { try { const paymentData = { From 1b26b5679299e7b9078083b3b25358ae6229615e Mon Sep 17 00:00:00 2001 From: guilhermer Date: Wed, 13 Mar 2024 13:50:55 +0100 Subject: [PATCH 5/5] fixed tests --- packages/lib/src/components/ApplePay/ApplePay.test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/lib/src/components/ApplePay/ApplePay.test.ts b/packages/lib/src/components/ApplePay/ApplePay.test.ts index 37607dc922..be8545e4ab 100644 --- a/packages/lib/src/components/ApplePay/ApplePay.test.ts +++ b/packages/lib/src/components/ApplePay/ApplePay.test.ts @@ -18,6 +18,11 @@ beforeEach(() => { }; }); +const configurationMock = { + merchantName: 'TestMerchant', + merchantId: 'test-merchant' +}; + describe('ApplePay', () => { describe('isExpress flag', () => { test('should add subtype: express when isExpress is configured', () => { @@ -47,6 +52,7 @@ describe('ApplePay', () => { }); const applepay = new ApplePay(global.core, { + configuration: configurationMock, amount: { currency: 'EUR', value: 2000 }, onPaymentFailed: onPaymentFailedMock, onSubmit(state, component, actions) { @@ -92,6 +98,7 @@ describe('ApplePay', () => { }); const applepay = new ApplePay(global.core, { + configuration: configurationMock, amount: { currency: 'EUR', value: 2000 }, onPaymentFailed: onPaymentFailedMock, onSubmit(state, component, actions) { @@ -143,6 +150,7 @@ describe('ApplePay', () => { }); const applepay = new ApplePay(global.core, { + configuration: configurationMock, amount: { currency: 'EUR', value: 2000 }, onOrderTrackingRequest: onOrderTrackingRequestMock, onPaymentCompleted: onPaymentCompletedMock @@ -193,6 +201,7 @@ describe('ApplePay', () => { }); const applepay = new ApplePay(global.core, { + configuration: configurationMock, amount: { currency: 'EUR', value: 2000 }, onOrderTrackingRequest: onOrderTrackingRequestMock, onPaymentCompleted: onPaymentCompletedMock @@ -237,6 +246,7 @@ describe('ApplePay', () => { }); const applepay = new ApplePay(global.core, { + configuration: configurationMock, amount: { currency: 'EUR', value: 2000 }, onOrderTrackingRequest: onOrderTrackingRequestMock, onPaymentCompleted: onPaymentCompletedMock @@ -313,6 +323,7 @@ describe('ApplePay', () => { }); const applepay = new ApplePay(global.core, { + configuration: configurationMock, amount: { currency: 'EUR', value: 2000 }, onAuthorized: onAuthorizedMock, onChange: onChangeMock,