diff --git a/.changeset/soft-clouds-knock.md b/.changeset/soft-clouds-knock.md new file mode 100644 index 00000000..6a0435d4 --- /dev/null +++ b/.changeset/soft-clouds-knock.md @@ -0,0 +1,5 @@ +--- +'@adyen/adyen-web': minor +--- + +Added environmentUrls parameter to Core, which allows PBL to use custom URLs for the API and assets diff --git a/packages/lib/src/components/Dropin/Dropin.test.ts b/packages/lib/src/components/Dropin/Dropin.test.ts index b9c39db1..aaddde8e 100644 --- a/packages/lib/src/components/Dropin/Dropin.test.ts +++ b/packages/lib/src/components/Dropin/Dropin.test.ts @@ -12,7 +12,7 @@ describe('Dropin', () => { let dropin: DropinElement; beforeEach(async () => { - const checkout = await AdyenCheckout({ analytics: { enabled: false } }); + const checkout = await AdyenCheckout({ environment: 'test', clientKey: 'test_123456', analytics: { enabled: false } }); dropin = checkout.create('dropin'); }); @@ -65,7 +65,7 @@ describe('Dropin', () => { }); test('should handle new challenge action', async () => { - const checkout = await AdyenCheckout({ analytics: { enabled: false } }); + const checkout = await AdyenCheckout({ environment: 'test', clientKey: 'test_123456', analytics: { enabled: false } }); const dropin = checkout.create('dropin'); @@ -77,7 +77,11 @@ describe('Dropin', () => { }); test('new challenge action gets challengeWindowSize from paymentMethodsConfiguration', async () => { - const checkout = await AdyenCheckout({ paymentMethodsConfiguration: { threeDS2: { challengeWindowSize: '02' } } }); + const checkout = await AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', + paymentMethodsConfiguration: { threeDS2: { challengeWindowSize: '02' } } + }); const dropin = checkout.create('dropin'); @@ -87,7 +91,12 @@ describe('Dropin', () => { }); test('new challenge action gets challengeWindowSize from handleAction config', async () => { - const checkout = await AdyenCheckout({ analytics: { enabled: false }, challengeWindowSize: '04' }); + const checkout = await AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', + analytics: { enabled: false }, + challengeWindowSize: '04' + }); const dropin = checkout.create('dropin'); mount(dropin.render()); @@ -103,6 +112,8 @@ describe('Dropin', () => { describe('Instant Payments feature', () => { test('formatProps formats instantPaymentTypes removing duplicates and invalid values', async () => { const checkout = await AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', analytics: { enabled: false } }); const dropin = checkout.create('dropin', { instantPaymentTypes: ['alipay', 'paywithgoogle', 'paywithgoogle', 'paypal'] }); @@ -112,6 +123,8 @@ describe('Dropin', () => { test('formatProps filter out instantPaymentMethods from paymentMethods list ', async () => { const checkout = await AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', analytics: { enabled: false }, paymentMethodsResponse: { paymentMethods: [ @@ -135,6 +148,8 @@ describe('Dropin', () => { ]; const checkout = await AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', analytics: { enabled: false }, paymentMethodsResponse: { paymentMethods @@ -153,6 +168,8 @@ describe('Dropin', () => { beforeEach(async () => { const paymentMethods = [{ name: 'AliPay', type: 'alipay' }]; const checkout = await AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', analytics: { enabled: false }, paymentMethodsResponse: { paymentMethods diff --git a/packages/lib/src/components/UIElement.test.ts b/packages/lib/src/components/UIElement.test.ts index bee63e78..1c2a07dc 100644 --- a/packages/lib/src/components/UIElement.test.ts +++ b/packages/lib/src/components/UIElement.test.ts @@ -137,7 +137,7 @@ describe('UIElement', () => { type: 'threeDS2' }; - const checkout = await AdyenCheckout({ analytics: { enabled: false } }); + const checkout = await AdyenCheckout({ environment: 'test', clientKey: 'test_123456', analytics: { enabled: false } }); const comp = checkout.create('card').mount('body'); const pa = comp.handleAction(fingerprintAction); @@ -151,6 +151,8 @@ describe('UIElement', () => { test('should handle new challenge action', async () => { const checkout = await AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', analytics: { enabled: false }, paymentMethodsConfiguration: { threeDS2: { challengeWindowSize: '02' } diff --git a/packages/lib/src/core/Environment/Environment.test.ts b/packages/lib/src/core/Environment/Environment.test.ts index 80cd0e4f..2fcf06e3 100644 --- a/packages/lib/src/core/Environment/Environment.test.ts +++ b/packages/lib/src/core/Environment/Environment.test.ts @@ -1,16 +1,47 @@ -import { resolveEnvironment } from './Environment'; +import { resolveCDNEnvironment, resolveEnvironment } from './Environment'; -describe('Environment', () => { - test('resolves set environments', () => { +describe('Resolving API environment', () => { + test('should return the proper URL according to the environment type', () => { expect(resolveEnvironment('test')).toBe('https://checkoutshopper-test.adyen.com/checkoutshopper/'); + expect(resolveEnvironment('TEST')).toBe('https://checkoutshopper-test.adyen.com/checkoutshopper/'); expect(resolveEnvironment('live')).toBe('https://checkoutshopper-live.adyen.com/checkoutshopper/'); + expect(resolveEnvironment('LIVE')).toBe('https://checkoutshopper-live.adyen.com/checkoutshopper/'); }); - test('resolves a URL environment', () => { - expect(resolveEnvironment('https://example.com/')).toBe('https://example.com/'); + test('should return environmentUrl if provided', () => { + expect(resolveEnvironment('devl', 'http://localhost:8080/checkoutshopper/')).toBe('http://localhost:8080/checkoutshopper/'); + expect(resolveEnvironment('test', 'https://checkout-zeta.com/checkoutshopper/')).toBe('https://checkout-zeta.com/checkoutshopper/'); }); - test('resolves to the live environment as a fallback', () => { + test('should return the live environment URL if environment type is not valid', () => { + expect(resolveEnvironment('invalid-environment')).toBe('https://checkoutshopper-live.adyen.com/checkoutshopper/'); + }); + + test('should return the live environment URL if environment type is not provided', () => { + // @ts-ignore It can happen that 'environment' property is not be passed by the merchant expect(resolveEnvironment()).toBe('https://checkoutshopper-live.adyen.com/checkoutshopper/'); }); }); + +describe('Resolving CDN Environment', () => { + test('should return the proper URL according to the environment type', () => { + expect(resolveCDNEnvironment('beta')).toBe('https://cdf6519016.cdn.adyen.com/checkoutshopper/'); + expect(resolveCDNEnvironment('BETA')).toBe('https://cdf6519016.cdn.adyen.com/checkoutshopper/'); + expect(resolveCDNEnvironment('live')).toBe('https://checkoutshopper-live.adyen.com/checkoutshopper/'); + expect(resolveCDNEnvironment('LIVE')).toBe('https://checkoutshopper-live.adyen.com/checkoutshopper/'); + }); + + test('should return environmentUrl if provided', () => { + expect(resolveCDNEnvironment('devl', 'http://localhost:8080/checkoutshopper/')).toBe('http://localhost:8080/checkoutshopper/'); + expect(resolveCDNEnvironment('beta', 'https://testing-beta-cdn.com/')).toBe('https://testing-beta-cdn.com/'); + }); + + test('should return the live environment URL if environment type is not valid', () => { + expect(resolveCDNEnvironment('invalid-environment')).toBe('https://checkoutshopper-live.adyen.com/checkoutshopper/'); + }); + + test('should return the live environment URL if environment type is not provided', () => { + // @ts-ignore It can happen that 'environment' property is not be passed by the merchant + expect(resolveCDNEnvironment()).toBe('https://checkoutshopper-live.adyen.com/checkoutshopper/'); + }); +}); diff --git a/packages/lib/src/core/Environment/Environment.ts b/packages/lib/src/core/Environment/Environment.ts index df44d12e..9fe9b3b6 100644 --- a/packages/lib/src/core/Environment/Environment.ts +++ b/packages/lib/src/core/Environment/Environment.ts @@ -1,6 +1,9 @@ export const FALLBACK_CONTEXT = 'https://checkoutshopper-live.adyen.com/checkoutshopper/'; +export const resolveEnvironment = (env = '', environmentUrl?: string): string => { + if (environmentUrl) { + return environmentUrl; + } -export const resolveEnvironment = (env: string = FALLBACK_CONTEXT): string => { const environments = { test: 'https://checkoutshopper-test.adyen.com/checkoutshopper/', live: 'https://checkoutshopper-live.adyen.com/checkoutshopper/', @@ -10,12 +13,15 @@ export const resolveEnvironment = (env: string = FALLBACK_CONTEXT): string => { 'live-in': 'https://checkoutshopper-live-in.adyen.com/checkoutshopper/' }; - return environments[env] || environments[env.toLowerCase()] || env; + return environments[env.toLowerCase()] || FALLBACK_CONTEXT; }; export const FALLBACK_CDN_CONTEXT = 'https://checkoutshopper-live.adyen.com/checkoutshopper/'; +export const resolveCDNEnvironment = (env = '', environmentUrl?: string) => { + if (environmentUrl) { + return environmentUrl; + } -export const resolveCDNEnvironment = (env: string = FALLBACK_CDN_CONTEXT) => { const environments = { beta: 'https://cdf6519016.cdn.adyen.com/checkoutshopper/', test: 'https://checkoutshopper-test.adyen.com/checkoutshopper/', @@ -26,5 +32,5 @@ export const resolveCDNEnvironment = (env: string = FALLBACK_CDN_CONTEXT) => { 'live-in': 'https://checkoutshopper-live-in.adyen.com/checkoutshopper/' }; - return environments[env] || environments[env.toLowerCase()] || env; + return environments[env.toLowerCase()] || FALLBACK_CDN_CONTEXT; }; diff --git a/packages/lib/src/core/core.component.props.test.ts b/packages/lib/src/core/core.component.props.test.ts index c50e0024..21a4030f 100644 --- a/packages/lib/src/core/core.component.props.test.ts +++ b/packages/lib/src/core/core.component.props.test.ts @@ -73,8 +73,8 @@ const checkoutConfig = { value: 19000 }, shopperLocale: 'en-US', - clientKey: 'test_F7_FEKJHF', environment: 'test', + clientKey: 'test_F7_FEKJHF', paymentMethodsResponse, paymentMethodsConfiguration: paymentMethodsConfiguration as PaymentMethodsConfiguration }; @@ -420,7 +420,7 @@ describe('Trying to add a "scheme" property to the paymentMethodsConfiguration t }; test('Trying to create a card component with a paymentMethodsConfiguration with a "scheme" property shows a warning in the console ', () => { - new AdyenCheckout({ paymentMethodsConfiguration }); + new AdyenCheckout({ environment: 'test', clientKey: 'test_123456', paymentMethodsConfiguration }); // expect warning in console expect(console.warn).toHaveBeenCalled(); }); diff --git a/packages/lib/src/core/core.test.ts b/packages/lib/src/core/core.test.ts index ed3121dc..538cca5e 100644 --- a/packages/lib/src/core/core.test.ts +++ b/packages/lib/src/core/core.test.ts @@ -30,14 +30,14 @@ beforeEach(() => { describe('Core', () => { test('should default to the FALLBACK_LOCALE', async () => { - const checkout = new AdyenCheckout({}); + const checkout = new AdyenCheckout({ environment: 'test', clientKey: 'test_123456' }); await checkout.initialize(); expect(checkout.modules.i18n.locale).toBe('en-US'); }); test('should create the modules when initializing on Advanced Flow', async () => { - const checkout = new AdyenCheckout({}); + const checkout = new AdyenCheckout({ environment: 'test', clientKey: 'test_123456' }); await checkout.initialize(); expect(Object.keys(checkout.modules).length).toBeGreaterThan(1); @@ -45,9 +45,9 @@ describe('Core', () => { test('should create the modules when initializing on Sesssions flow', async () => { const checkout = new AdyenCheckout({ - session: { id: 'session-id', sessionData: 'sesssion-data' }, environment: 'test', - clientKey: 'xxxx' + clientKey: 'test_123456', + session: { id: 'session-id', sessionData: 'sesssion-data' } }); await checkout.initialize(); @@ -56,7 +56,7 @@ describe('Core', () => { }); test('should set a custom locale', async () => { - const checkout = new AdyenCheckout({ locale: 'es-ES' }); + const checkout = new AdyenCheckout({ environment: 'test', clientKey: 'test_123456', locale: 'es-ES' }); await checkout.initialize(); expect(checkout.modules.i18n.locale).toBe('es-ES'); @@ -64,7 +64,7 @@ describe('Core', () => { describe('create', () => { test('should create a component if it exists', async () => { - const checkout = new AdyenCheckout({}); + const checkout = new AdyenCheckout({ environment: 'test', clientKey: 'test_123456' }); await checkout.initialize(); expect(checkout.create('dropin')).toBeTruthy(); @@ -77,7 +77,7 @@ describe('Core', () => { const onSubmitMockComponent = jest.fn().mockName('onSubmitMockComponent'); test('component props receive global props if not defined elsewhere', async () => { - const checkout = new AdyenCheckout({ onSubmit: onSubmitMockGlobal }); + const checkout = new AdyenCheckout({ environment: 'test', clientKey: 'test_123456', onSubmit: onSubmitMockGlobal }); await checkout.initialize(); const component = checkout.create('card'); @@ -85,7 +85,7 @@ describe('Core', () => { }); test('component props take precedence over global props', async () => { - const checkout = new AdyenCheckout({ onSubmit: onSubmitMockGlobal }); + const checkout = new AdyenCheckout({ environment: 'test', clientKey: 'test_123456', onSubmit: onSubmitMockGlobal }); await checkout.initialize(); const component = checkout.create('card', { onSubmit: onSubmitMockComponent }); @@ -94,6 +94,8 @@ describe('Core', () => { test('paymentMethodsConfiguration props take precedence over global props', async () => { const checkout = new AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', onSubmit: onSubmitMockGlobal, paymentMethodsConfiguration: { card: { onSubmit: onSubmitMockPMConfig } } }); @@ -105,6 +107,8 @@ describe('Core', () => { test('component props take precedence over paymentMethodsConfiguration props', async () => { const checkout = new AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', paymentMethodsConfiguration: { card: { onSubmit: onSubmitMockPMConfig } } }); await checkout.initialize(); @@ -117,7 +121,10 @@ describe('Core', () => { describe('createFromAction', () => { test('should create a component from an action object', async () => { - const checkout = new AdyenCheckout({}); + const checkout = new AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456' + }); await checkout.initialize(); const paymentAction = checkout.createFromAction({ @@ -131,7 +138,11 @@ describe('Core', () => { }); test('should handle new fingerprint action', async () => { - const checkout = new AdyenCheckout({ paymentMethodsConfiguration: { threeDS2: { challengeWindowSize: '04' } } }); + const checkout = new AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', + paymentMethodsConfiguration: { threeDS2: { challengeWindowSize: '04' } } + }); await checkout.initialize(); const fingerprintAction = { @@ -155,6 +166,8 @@ describe('Core', () => { test('should handle new challenge action', async () => { const checkout = new AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', paymentMethodsConfiguration: { threeDS2: { challengeWindowSize: '03' @@ -187,6 +200,8 @@ describe('Core', () => { test('paymentMethodsConfiguration properties take precedence over global configuration', async () => { const checkout = new AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', onAdditionalDetails: onAdditionalDetailsGlobal, paymentMethodsConfiguration: { qrCode: { onAdditionalDetails: onAdditionalDetailsBCMC } } }); @@ -203,6 +218,8 @@ describe('Core', () => { test('createFromAction props take precedence over paymentMethodsConfiguration and global configuration', async () => { const checkout = new AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456', onAdditionalDetails: onAdditionalDetailsGlobal, paymentMethodsConfiguration: { qrCode: { onAdditionalDetails: onAdditionalDetailsBCMC } } }); @@ -226,7 +243,10 @@ describe('Core', () => { describe('update', () => { test('Should update all components under main instance', async () => { - const checkout = new AdyenCheckout({}); + const checkout = new AdyenCheckout({ + environment: 'test', + clientKey: 'test_123456' + }); await checkout.initialize(); const component = checkout.create('dropin').mount('body'); @@ -249,4 +269,16 @@ describe('Core', () => { expect(checkout.paymentMethodsResponse).toHaveProperty('paymentMethods', paymentMethodsResponse.paymentMethods); }); }); + + test('should use custom checkoutshopper URL url if available', () => { + const checkout = new AdyenCheckout({ + environment: 'test', + environmentUrls: { + api: 'https://localhost:8080/checkoutshopper/' + }, + clientKey: 'devl_FX923810' + }); + + expect(checkout.loadingContext).toBe('https://localhost:8080/checkoutshopper/'); + }); }); diff --git a/packages/lib/src/core/core.ts b/packages/lib/src/core/core.ts index 5975b276..a1c87011 100644 --- a/packages/lib/src/core/core.ts +++ b/packages/lib/src/core/core.ts @@ -39,12 +39,12 @@ class Core { this.setOptions(props); - this.loadingContext = resolveEnvironment(this.options.environment); - this.cdnContext = resolveCDNEnvironment(this.options.resourceEnvironment || this.options.environment); + this.loadingContext = resolveEnvironment(this.options.environment, this.options.environmentUrls?.api); + this.cdnContext = resolveCDNEnvironment(this.options.resourceEnvironment || this.options.environment, this.options.environmentUrls?.api); const clientKeyType = this.options.clientKey?.substr(0, 4); if ((clientKeyType === 'test' || clientKeyType === 'live') && !this.loadingContext.includes(clientKeyType)) { - throw new Error(`Error: you are using a ${clientKeyType} clientKey against the ${this.options.environment} environment`); + throw new Error(`Error: you are using a '${clientKeyType}' clientKey against the '${this.options.environment}' environment`); } // Expose version number for npm builds diff --git a/packages/lib/src/core/types.ts b/packages/lib/src/core/types.ts index 21781005..1fc54085 100644 --- a/packages/lib/src/core/types.ts +++ b/packages/lib/src/core/types.ts @@ -20,6 +20,16 @@ export interface CoreOptions { */ environment?: 'test' | 'live' | 'live-us' | 'live-au' | 'live-apse' | 'live-in' | string; + /** + * Used internally by Pay By Link in order to set its own URL's instead of using the ones mapped in our codebase. + * + * @internal + */ + environmentUrls?: { + api?: string; + analytics?: string; + }; + /** * Show or hides a Pay Button for each payment method */