diff --git a/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/create-paypal-commerce-alternative-methods-button-strategy.ts b/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/create-paypal-commerce-alternative-methods-button-strategy.ts index 52033509fd..c3283c9c86 100644 --- a/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/create-paypal-commerce-alternative-methods-button-strategy.ts +++ b/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/create-paypal-commerce-alternative-methods-button-strategy.ts @@ -3,7 +3,7 @@ import { toResolvableModule, } from '@bigcommerce/checkout-sdk/payment-integration-api'; -import createPayPalCommerceIntegrationService from '../create-paypal-commerce-integration-service'; +import { createPayPalIntegrationService } from '@bigcommerce/checkout-sdk/paypal-utils'; import PayPalCommerceAlternativeMethodsButtonStrategy from './paypal-commerce-alternative-methods-button-strategy'; @@ -12,7 +12,7 @@ const createPayPalCommerceAlternativeMethodsButtonStrategy: CheckoutButtonStrate > = (paymentIntegrationService) => new PayPalCommerceAlternativeMethodsButtonStrategy( paymentIntegrationService, - createPayPalCommerceIntegrationService(paymentIntegrationService), + createPayPalIntegrationService(paymentIntegrationService), ); export default toResolvableModule(createPayPalCommerceAlternativeMethodsButtonStrategy, [ diff --git a/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/paypal-commerce-alternative-methods-button-strategy.spec.ts b/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/paypal-commerce-alternative-methods-button-strategy.spec.ts index 52eb43687b..6b3685c05a 100644 --- a/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/paypal-commerce-alternative-methods-button-strategy.spec.ts +++ b/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/paypal-commerce-alternative-methods-button-strategy.spec.ts @@ -13,18 +13,15 @@ import { getCart, PaymentIntegrationServiceMock, } from '@bigcommerce/checkout-sdk/payment-integrations-test-utils'; - import { - getPayPalCommerceIntegrationServiceMock, - getPayPalCommercePaymentMethod, + getPayPalIntegrationServiceMock, + getPayPalPaymentMethod, getPayPalSDKMock, -} from '../mocks'; -import PayPalCommerceIntegrationService from '../paypal-commerce-integration-service'; -import { - PayPalCommerceButtonsOptions, - PayPalCommerceHostWindow, + PayPalButtonsOptions, + PayPalHostWindow, + PayPalIntegrationService, PayPalSDK, -} from '../paypal-commerce-types'; +} from '@bigcommerce/checkout-sdk/paypal-utils'; import PayPalCommerceAlternativeMethodsButtonOptions from './paypal-commerce-alternative-methods-button-initialize-options'; import PayPalCommerceAlternativeMethodsButtonStrategy from './paypal-commerce-alternative-methods-button-strategy'; @@ -37,7 +34,7 @@ describe('PayPalCommerceAlternativeMethodsButtonStrategy', () => { let paymentIntegrationService: PaymentIntegrationService; let paymentMethod: PaymentMethod; let paypalButtonElement: HTMLDivElement; - let paypalCommerceIntegrationService: PayPalCommerceIntegrationService; + let paypalIntegrationService: PayPalIntegrationService; let paypalSdk: PayPalSDK; const defaultMethodId = 'paypalcommercealternativemethods'; @@ -85,15 +82,15 @@ describe('PayPalCommerceAlternativeMethodsButtonStrategy', () => { eventEmitter = new EventEmitter(); - paypalCommerceIntegrationService = getPayPalCommerceIntegrationServiceMock(); - paymentMethod = { ...getPayPalCommercePaymentMethod(), id: defaultMethodId }; + paypalIntegrationService = getPayPalIntegrationServiceMock(); + paymentMethod = { ...getPayPalPaymentMethod(), id: defaultMethodId }; paypalSdk = getPayPalSDKMock(); paymentIntegrationService = new PaymentIntegrationServiceMock(); strategy = new PayPalCommerceAlternativeMethodsButtonStrategy( paymentIntegrationService, - paypalCommerceIntegrationService, + paypalIntegrationService, ); paypalButtonElement = document.createElement('div'); @@ -105,93 +102,87 @@ describe('PayPalCommerceAlternativeMethodsButtonStrategy', () => { paymentMethod, ); - jest.spyOn(paypalCommerceIntegrationService, 'loadPayPalSdk').mockReturnValue( + jest.spyOn(paypalIntegrationService, 'loadPayPalSdk').mockReturnValue( Promise.resolve(paypalSdk), ); - jest.spyOn(paypalCommerceIntegrationService, 'getPayPalSdkOrThrow').mockReturnValue( - paypalSdk, - ); - jest.spyOn(paypalCommerceIntegrationService, 'createBuyNowCartOrThrow').mockReturnValue( + jest.spyOn(paypalIntegrationService, 'getPayPalSdkOrThrow').mockReturnValue(paypalSdk); + jest.spyOn(paypalIntegrationService, 'createBuyNowCartOrThrow').mockReturnValue( Promise.resolve(buyNowCart), ); - jest.spyOn(paypalCommerceIntegrationService, 'createOrder'); - jest.spyOn(paypalCommerceIntegrationService, 'tokenizePayment').mockImplementation( - jest.fn(), - ); - jest.spyOn(paypalCommerceIntegrationService, 'removeElement').mockImplementation(jest.fn()); + jest.spyOn(paypalIntegrationService, 'createOrder'); + jest.spyOn(paypalIntegrationService, 'tokenizePayment').mockImplementation(jest.fn()); + jest.spyOn(paypalIntegrationService, 'removeElement').mockImplementation(jest.fn()); + + jest.spyOn(paypalSdk, 'Buttons').mockImplementation((options: PayPalButtonsOptions) => { + eventEmitter.on('createOrder', () => { + if (options.createOrder) { + options.createOrder(); + } + }); + + eventEmitter.on( + 'onClick', + // eslint-disable-next-line @typescript-eslint/no-misused-promises + async (jestSuccessExpectationsCallback, jestFailureExpectationsCallback) => { + try { + if (options.onClick) { + await options.onClick( + { fundingSource: apmProviderId }, + { + reject: jest.fn(), + resolve: jest.fn(), + }, + ); - jest.spyOn(paypalSdk, 'Buttons').mockImplementation( - (options: PayPalCommerceButtonsOptions) => { - eventEmitter.on('createOrder', () => { - if (options.createOrder) { - options.createOrder(); - } - }); - - eventEmitter.on( - 'onClick', - // eslint-disable-next-line @typescript-eslint/no-misused-promises - async (jestSuccessExpectationsCallback, jestFailureExpectationsCallback) => { - try { - if (options.onClick) { - await options.onClick( - { fundingSource: apmProviderId }, - { - reject: jest.fn(), - resolve: jest.fn(), - }, - ); - - if ( - jestSuccessExpectationsCallback && - typeof jestSuccessExpectationsCallback === 'function' - ) { - jestSuccessExpectationsCallback(); - } - } - } catch (error) { if ( - jestFailureExpectationsCallback && - typeof jestFailureExpectationsCallback === 'function' + jestSuccessExpectationsCallback && + typeof jestSuccessExpectationsCallback === 'function' ) { - jestFailureExpectationsCallback(error); + jestSuccessExpectationsCallback(); } } - }, - ); - - eventEmitter.on('onApprove', () => { - if (options.onApprove) { - options.onApprove( - { orderID: paypalOrderId }, - { - order: { - get: jest.fn(), - }, - }, - ); + } catch (error) { + if ( + jestFailureExpectationsCallback && + typeof jestFailureExpectationsCallback === 'function' + ) { + jestFailureExpectationsCallback(error); + } } - }); + }, + ); - eventEmitter.on('onCancel', () => { - if (options.onCancel) { - options.onCancel(); - } - }); + eventEmitter.on('onApprove', () => { + if (options.onApprove) { + options.onApprove( + { orderID: paypalOrderId }, + { + order: { + get: jest.fn(), + }, + }, + ); + } + }); - return { - isEligible: jest.fn(() => true), - render: jest.fn(), - close: jest.fn(), - }; - }, - ); + eventEmitter.on('onCancel', () => { + if (options.onCancel) { + options.onCancel(); + } + }); + + return { + isEligible: jest.fn(() => true), + render: jest.fn(), + close: jest.fn(), + }; + }); }); afterEach(() => { jest.clearAllMocks(); - delete (window as PayPalCommerceHostWindow).paypal; + delete (window as PayPalHostWindow).paypal; if (document.getElementById(defaultButtonContainerId)) { document.body.removeChild(paypalButtonElement); @@ -308,7 +299,7 @@ describe('PayPalCommerceAlternativeMethodsButtonStrategy', () => { it('loads paypal commerce sdk script', async () => { await strategy.initialize(initializationOptions); - expect(paypalCommerceIntegrationService.loadPayPalSdk).toHaveBeenCalledWith( + expect(paypalIntegrationService.loadPayPalSdk).toHaveBeenCalledWith( defaultMethodId, cart.currency.code, false, @@ -318,7 +309,7 @@ describe('PayPalCommerceAlternativeMethodsButtonStrategy', () => { it('loads paypal commerce sdk script with provided currency code (Buy Now flow)', async () => { await strategy.initialize(buyNowInitializationOptions); - expect(paypalCommerceIntegrationService.loadPayPalSdk).toHaveBeenCalledWith( + expect(paypalIntegrationService.loadPayPalSdk).toHaveBeenCalledWith( defaultMethodId, buyNowPayPalCommerceAlternativeMethodsOptions.currencyCode, false, @@ -416,7 +407,7 @@ describe('PayPalCommerceAlternativeMethodsButtonStrategy', () => { }, }); - expect(paypalCommerceIntegrationService.removeElement).toHaveBeenCalledWith( + expect(paypalIntegrationService.removeElement).toHaveBeenCalledWith( defaultButtonContainerId, ); }); @@ -430,7 +421,7 @@ describe('PayPalCommerceAlternativeMethodsButtonStrategy', () => { await new Promise((resolve) => process.nextTick(resolve)); - expect(paypalCommerceIntegrationService.createOrder).toHaveBeenCalledWith( + expect(paypalIntegrationService.createOrder).toHaveBeenCalledWith( 'paypalcommercealternativemethod', ); }); @@ -449,7 +440,7 @@ describe('PayPalCommerceAlternativeMethodsButtonStrategy', () => { eventEmitter.emit('onClick'); await new Promise((resolve) => process.nextTick(resolve)); - expect(paypalCommerceIntegrationService.createBuyNowCartOrThrow).toHaveBeenCalled(); + expect(paypalIntegrationService.createBuyNowCartOrThrow).toHaveBeenCalled(); }); it('loads checkout related to buy now cart on button click', async () => { @@ -469,7 +460,7 @@ describe('PayPalCommerceAlternativeMethodsButtonStrategy', () => { await new Promise((resolve) => process.nextTick(resolve)); - expect(paypalCommerceIntegrationService.tokenizePayment).toHaveBeenCalledWith( + expect(paypalIntegrationService.tokenizePayment).toHaveBeenCalledWith( defaultMethodId, paypalOrderId, ); diff --git a/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/paypal-commerce-alternative-methods-button-strategy.ts b/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/paypal-commerce-alternative-methods-button-strategy.ts index a7175fa97a..d6aeff977b 100644 --- a/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/paypal-commerce-alternative-methods-button-strategy.ts +++ b/packages/paypal-commerce-integration/src/paypal-commerce-alternative-methods/paypal-commerce-alternative-methods-button-strategy.ts @@ -4,8 +4,8 @@ import { InvalidArgumentError, PaymentIntegrationService, } from '@bigcommerce/checkout-sdk/payment-integration-api'; +import { PayPalIntegrationService } from '@bigcommerce/checkout-sdk/paypal-utils'; -import PayPalCommerceIntegrationService from '../paypal-commerce-integration-service'; import { ApproveCallbackPayload, PayPalBuyNowInitializeOptions, @@ -21,7 +21,7 @@ export default class PayPalCommerceAlternativeMethodsButtonStrategy { constructor( private paymentIntegrationService: PaymentIntegrationService, - private paypalCommerceIntegrationService: PayPalCommerceIntegrationService, + private paypalIntegrationService: PayPalIntegrationService, ) {} async initialize( @@ -89,7 +89,7 @@ export default class PayPalCommerceAlternativeMethodsButtonStrategy ? providedCurrencyCode : this.paymentIntegrationService.getState().getCartOrThrow().currency.code; - await this.paypalCommerceIntegrationService.loadPayPalSdk(methodId, currencyCode, false); + await this.paypalIntegrationService.loadPayPalSdk(methodId, currencyCode, false); this.renderButton(containerId, methodId, paypalcommercealternativemethods); } @@ -106,7 +106,7 @@ export default class PayPalCommerceAlternativeMethodsButtonStrategy const { apm, buyNowInitializeOptions, style, onEligibilityFailure } = paypalcommercealternativemethods; - const paypalSdk = this.paypalCommerceIntegrationService.getPayPalSdkOrThrow(); + const paypalSdk = this.paypalIntegrationService.getPayPalSdkOrThrow(); const isAvailableFundingSource = Object.values(paypalSdk.FUNDING).includes(apm); if (!isAvailableFundingSource) { @@ -117,11 +117,9 @@ export default class PayPalCommerceAlternativeMethodsButtonStrategy const defaultCallbacks = { createOrder: () => - this.paypalCommerceIntegrationService.createOrder( - 'paypalcommercealternativemethod', - ), + this.paypalIntegrationService.createOrder('paypalcommercealternativemethod'), onApprove: ({ orderID }: ApproveCallbackPayload) => - this.paypalCommerceIntegrationService.tokenizePayment(methodId, orderID), + this.paypalIntegrationService.tokenizePayment(methodId, orderID), }; const buyNowFlowCallbacks = { @@ -131,7 +129,7 @@ export default class PayPalCommerceAlternativeMethodsButtonStrategy const buttonRenderOptions: PayPalCommerceButtonsOptions = { fundingSource: apm, - style: this.paypalCommerceIntegrationService.getValidButtonStyle(style), + style: this.paypalIntegrationService.getValidButtonStyle(style), ...defaultCallbacks, ...(buyNowInitializeOptions && buyNowFlowCallbacks), }; @@ -143,7 +141,7 @@ export default class PayPalCommerceAlternativeMethodsButtonStrategy } else if (onEligibilityFailure && typeof onEligibilityFailure === 'function') { onEligibilityFailure(); } else { - this.paypalCommerceIntegrationService.removeElement(containerId); + this.paypalIntegrationService.removeElement(containerId); } } @@ -151,7 +149,7 @@ export default class PayPalCommerceAlternativeMethodsButtonStrategy buyNowInitializeOptions?: PayPalBuyNowInitializeOptions, ): Promise { if (buyNowInitializeOptions) { - const buyNowCart = await this.paypalCommerceIntegrationService.createBuyNowCartOrThrow( + const buyNowCart = await this.paypalIntegrationService.createBuyNowCartOrThrow( buyNowInitializeOptions, ); diff --git a/packages/paypal-utils/src/create-paypal-integration-service.spec.ts b/packages/paypal-utils/src/create-paypal-integration-service.spec.ts new file mode 100644 index 0000000000..95ab5d6908 --- /dev/null +++ b/packages/paypal-utils/src/create-paypal-integration-service.spec.ts @@ -0,0 +1,19 @@ +import { PaymentIntegrationService } from '@bigcommerce/checkout-sdk/payment-integration-api'; +import { PaymentIntegrationServiceMock } from '@bigcommerce/checkout-sdk/payment-integrations-test-utils'; + +import createPayPalIntegrationService from './create-paypal-integration-service'; +import PaypalIntegrationService from './paypal-integration-service'; + +describe('createPayPalIntegrationService', () => { + let paymentIntegrationService: PaymentIntegrationService; + + beforeEach(() => { + paymentIntegrationService = new PaymentIntegrationServiceMock(); + }); + + it('instantiates PayPal integration service', () => { + const service = createPayPalIntegrationService(paymentIntegrationService); + + expect(service).toBeInstanceOf(PaypalIntegrationService); + }); +}); diff --git a/packages/paypal-utils/src/create-paypal-integration-service.ts b/packages/paypal-utils/src/create-paypal-integration-service.ts new file mode 100644 index 0000000000..a1067a3829 --- /dev/null +++ b/packages/paypal-utils/src/create-paypal-integration-service.ts @@ -0,0 +1,21 @@ +import { createFormPoster } from '@bigcommerce/form-poster'; +import { createRequestSender } from '@bigcommerce/request-sender'; + +import { PaymentIntegrationService } from '@bigcommerce/checkout-sdk/payment-integration-api'; + +import createPayPalSdkScriptLoader from './create-paypal-sdk-script-loader'; +import PaypalIntegrationService from './paypal-integration-service'; +import PaypalRequestSender from './paypal-request-sender'; + +const createPayPalIntegrationService = (paymentIntegrationService: PaymentIntegrationService) => { + const { getHost } = paymentIntegrationService.getState(); + + return new PaypalIntegrationService( + createFormPoster(), + paymentIntegrationService, + new PaypalRequestSender(createRequestSender({ host: getHost() })), + createPayPalSdkScriptLoader(), + ); +}; + +export default createPayPalIntegrationService; diff --git a/packages/paypal-utils/src/index.ts b/packages/paypal-utils/src/index.ts index 16ad2f8c79..7f0c1a24c1 100644 --- a/packages/paypal-utils/src/index.ts +++ b/packages/paypal-utils/src/index.ts @@ -20,3 +20,13 @@ export { default as PayPalSdkScriptLoader } from './paypal-sdk-script-loader'; */ export { default as createPayPalFastlaneUtils } from './create-paypal-fastlane-utils'; export { default as PayPalFastlaneUtils } from './paypal-fastlane-utils'; + + +/** + * + * PayPal Integration Service exports + * + */ + +export { default as PayPalIntegrationService } from './paypal-integration-service'; +export { default as createPayPalIntegrationService } from './create-paypal-integration-service'; diff --git a/packages/paypal-utils/src/mocks/get-paypal-integration-service-mock.mock.ts b/packages/paypal-utils/src/mocks/get-paypal-integration-service-mock.mock.ts index 8a6c6c3baa..4b83ad5a91 100644 --- a/packages/paypal-utils/src/mocks/get-paypal-integration-service-mock.mock.ts +++ b/packages/paypal-utils/src/mocks/get-paypal-integration-service-mock.mock.ts @@ -8,7 +8,7 @@ import PayPalIntegrationService from '../paypal-integration-service'; import PayPalRequestSender from '../paypal-request-sender'; import PayPalSdkLoader from '../paypal-sdk-script-loader'; -export default function getPayPalCommerceIntegrationServiceMock(): PayPalIntegrationService { +export default function getPayPalIntegrationServiceMock(): PayPalIntegrationService { const formPoster = createFormPoster(); const requestSender = createRequestSender(); const paymentIntegrationService = new PaymentIntegrationServiceMock();