Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from '@bigcommerce/checkout-sdk/payment-integration-api';
import {
getCart,
getConfig,
getErrorPaymentResponseBody,
getOrderRequestBody,
getResponse,
Expand All @@ -30,6 +31,8 @@ describe('AfterpayPaymentStrategy', () => {
let paymentMethod: PaymentMethod;
let scriptLoader: AfterpayScriptLoader;
let strategy: AfterpayPaymentStrategy;
const storeConfig = getConfig().storeConfig;
const withHttpsExperimentName = 'PI-4789.afterpay_script_use_https';

const afterpaySdk = {
initialize: jest.fn(),
Expand Down Expand Up @@ -66,6 +69,17 @@ describe('AfterpayPaymentStrategy', () => {
jest.spyOn(paymentIntegrationService.getState(), 'getPaymentMethod').mockReturnValue(
paymentMethod,
);

jest.spyOn(paymentIntegrationService.getState(), 'getStoreConfigOrThrow').mockReturnValue({
...storeConfig,
checkoutSettings: {
...storeConfig.checkoutSettings,
features: {
...storeConfig.checkoutSettings.features,
[withHttpsExperimentName]: true,
},
},
});
});

afterEach(() => {
Expand All @@ -92,7 +106,30 @@ describe('AfterpayPaymentStrategy', () => {
gatewayId: paymentMethod.gateway,
});

expect(scriptLoader.load).toHaveBeenCalledWith(paymentMethod, 'US');
expect(scriptLoader.load).toHaveBeenCalledWith(paymentMethod, 'US', true);
});

it('loads script when initializing strategy with NO https', async () => {
jest.spyOn(
paymentIntegrationService.getState(),
'getStoreConfigOrThrow',
).mockReturnValue({
...storeConfig,
checkoutSettings: {
...storeConfig.checkoutSettings,
features: {
...storeConfig.checkoutSettings.features,
[withHttpsExperimentName]: false,
},
},
});

await strategy.initialize({
methodId: paymentMethod.id,
gatewayId: paymentMethod.gateway,
});

expect(scriptLoader.load).toHaveBeenCalledWith(paymentMethod, 'US', false);
});

it('loads script when initializing strategy with NZD', async () => {
Expand All @@ -106,7 +143,7 @@ describe('AfterpayPaymentStrategy', () => {
gatewayId: paymentMethod.gateway,
});

expect(scriptLoader.load).toHaveBeenCalledWith(paymentMethod, 'NZ');
expect(scriptLoader.load).toHaveBeenCalledWith(paymentMethod, 'NZ', true);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
RequestError,
RequestOptions,
} from '@bigcommerce/checkout-sdk/payment-integration-api';
import { isExperimentEnabled } from '@bigcommerce/checkout-sdk/utility';

import AfterpayScriptLoader from './afterpay-script-loader';
import AfterpaySdk from './afterpay-sdk';
Expand All @@ -34,12 +35,19 @@ export default class AfterpayPaymentStrategy implements PaymentStrategy {
const paymentMethod = state.getPaymentMethod(options.methodId, options.gatewayId);
const currencyCode = state.getCart()?.currency.code || '';
const countryCode = this._mapCurrencyToISO2(currencyCode);
const features = state.getStoreConfigOrThrow().checkoutSettings.features;
const withHttpsExperimentName = 'PI-4789.afterpay_script_use_https';
const withHttps = isExperimentEnabled(features, withHttpsExperimentName, false);

if (!paymentMethod) {
throw new MissingDataError(MissingDataErrorType.MissingPaymentMethod);
}

this._afterpaySdk = await this._afterpayScriptLoader.load(paymentMethod, countryCode);
this._afterpaySdk = await this._afterpayScriptLoader.load(
paymentMethod,
countryCode,
withHttps,
);
}

deinitialize(): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('AfterpayScriptLoader', () => {
'//portal.afterpay.com/afterpay-async.js',
);

await afterpayScriptLoader.load(method, 'NZ');
await afterpayScriptLoader.load(method, 'NZ', false);

expect(scriptLoader.loadScript).toHaveBeenCalledWith(
'//portal.afterpay.com/afterpay-async.js',
Expand All @@ -38,7 +38,7 @@ describe('AfterpayScriptLoader', () => {
it('loads sandbox widget script if in test mode for AU & NZ', async () => {
const method = merge({}, getAfterpay(), { config: { testMode: true } });

await afterpayScriptLoader.load(method, 'AU');
await afterpayScriptLoader.load(method, 'AU', false);

expect(scriptLoader.loadScript).toHaveBeenCalledWith(
'//portal.sandbox.afterpay.com/afterpay.js',
Expand All @@ -54,7 +54,7 @@ describe('AfterpayScriptLoader', () => {
it('loads widget script for US', async () => {
const method = getAfterpay();

await afterpayScriptLoader.load(method, 'US');
await afterpayScriptLoader.load(method, 'US', false);

expect(scriptLoader.loadScript).toHaveBeenCalledWith(
'//portal.afterpay.com/afterpay-async.js',
Expand All @@ -64,10 +64,62 @@ describe('AfterpayScriptLoader', () => {
it('loads sandbox widget script if in test mode for US', async () => {
const method = merge({}, getAfterpay(), { config: { testMode: true } });

await afterpayScriptLoader.load(method, 'US');
await afterpayScriptLoader.load(method, 'US', false);

expect(scriptLoader.loadScript).toHaveBeenCalledWith(
'//portal.sandbox.afterpay.com/afterpay.js',
);
});

it('loads widget script with HTTPS for AU & NZ', async () => {
const method = getAfterpay();

await afterpayScriptLoader.load(method, 'AU', true);

expect(scriptLoader.loadScript).toHaveBeenCalledWith(
'https://portal.afterpay.com/afterpay-async.js',
);

await afterpayScriptLoader.load(method, 'NZ');

expect(scriptLoader.loadScript).toHaveBeenCalledWith(
'https://portal.afterpay.com/afterpay-async.js',
);
});

it('loads sandbox widget script with HTTPS if in test mode for AU & NZ', async () => {
const method = merge({}, getAfterpay(), { config: { testMode: true } });

await afterpayScriptLoader.load(method, 'AU', true);

expect(scriptLoader.loadScript).toHaveBeenCalledWith(
'https://portal.sandbox.afterpay.com/afterpay.js',
);

await afterpayScriptLoader.load(method, 'NZ');

expect(scriptLoader.loadScript).toHaveBeenCalledWith(
'https://portal.sandbox.afterpay.com/afterpay.js',
);
});

it('loads widget script with HTTPS for US', async () => {
const method = getAfterpay();

await afterpayScriptLoader.load(method, 'US', true);

expect(scriptLoader.loadScript).toHaveBeenCalledWith(
'https://portal.afterpay.com/afterpay-async.js',
);
});

it('loads sandbox widget script with HTTPS if in test mode for US', async () => {
const method = merge({}, getAfterpay(), { config: { testMode: true } });

await afterpayScriptLoader.load(method, 'US', true);

expect(scriptLoader.loadScript).toHaveBeenCalledWith(
'https://portal.sandbox.afterpay.com/afterpay.js',
);
});
});
22 changes: 19 additions & 3 deletions packages/afterpay-integration/src/afterpay-script-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ import isAfterpayWindow from './is-afterpay-window';
enum SCRIPTS_DEFAULT {
PROD = '//portal.afterpay.com/afterpay-async.js',
SANDBOX = '//portal.sandbox.afterpay.com/afterpay.js',
HTTPS_PROD = 'https://portal.afterpay.com/afterpay-async.js',
HTTPS_SANDBOX = 'https://portal.sandbox.afterpay.com/afterpay.js',
}

enum SCRIPTS_US {
PROD = '//portal.afterpay.com/afterpay-async.js',
SANDBOX = '//portal.sandbox.afterpay.com/afterpay.js',
HTTPS_PROD = 'https://portal.afterpay.com/afterpay-async.js',
HTTPS_SANDBOX = 'https://portal.sandbox.afterpay.com/afterpay.js',
}

/** Class responsible for loading the Afterpay SDK */
Expand All @@ -27,9 +31,13 @@ export default class AfterpayScriptLoader {
*
* @param {PaymentMethod} method the payment method data
*/
async load(method: PaymentMethod, countryCode: string): Promise<AfterpaySdk> {
async load(
method: PaymentMethod,
countryCode: string,
withHttps = false,
): Promise<AfterpaySdk> {
const testMode = method.config.testMode || false;
const scriptURI = this._getScriptURI(countryCode, testMode);
const scriptURI = this._getScriptURI(countryCode, testMode, withHttps);

return this._scriptLoader.loadScript(scriptURI).then(() => {
if (!isAfterpayWindow(window)) {
Expand All @@ -40,11 +48,19 @@ export default class AfterpayScriptLoader {
});
}

private _getScriptURI(countryCode: string, testMode: boolean): string {
private _getScriptURI(countryCode: string, testMode: boolean, withHttps = false): string {
if (countryCode === 'US') {
if (withHttps) {
return testMode ? SCRIPTS_US.HTTPS_SANDBOX : SCRIPTS_US.HTTPS_PROD;
}

return testMode ? SCRIPTS_US.SANDBOX : SCRIPTS_US.PROD;
}

if (withHttps) {
return testMode ? SCRIPTS_DEFAULT.HTTPS_SANDBOX : SCRIPTS_DEFAULT.HTTPS_PROD;
}

return testMode ? SCRIPTS_DEFAULT.SANDBOX : SCRIPTS_DEFAULT.PROD;
}
}