diff --git a/package-lock.json b/package-lock.json index dbcd18329a..d99f92aa67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -838,9 +838,9 @@ "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" }, "regenerator-runtime": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.6.tgz", - "integrity": "sha512-GmwlGiazQEbOwQWDdbbaP10i15pGtScYWLbMZuu+RKRz0cZ+g8IUONazBnaZqe7j1670IV1HgE4/8iy7CQPf4Q==" + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" } } }, @@ -1609,9 +1609,9 @@ } }, "@bigcommerce/checkout-sdk": { - "version": "1.83.0", - "resolved": "https://registry.npmjs.org/@bigcommerce/checkout-sdk/-/checkout-sdk-1.83.0.tgz", - "integrity": "sha512-RqTn3hjP1lhSdpovhwrrCajpSbIzmzK7MzMPRsYAE2mPVaPuoJ1DrtUY2DqQXQuddZIHjGK4CpYRtF6TXljSng==", + "version": "1.84.0", + "resolved": "https://registry.npmjs.org/@bigcommerce/checkout-sdk/-/checkout-sdk-1.84.0.tgz", + "integrity": "sha512-hACODL85dGx/Az3eNqYJzBtDpfFTHuItMP/LHeaUVMvt+Z5QIuFEJVbBuFgE/p+A8C8xw+qnKgAKu8lYJzv/pQ==", "requires": { "@babel/polyfill": "^7.4.4", "@bigcommerce/bigpay-client": "^5.9.0", @@ -1806,7 +1806,7 @@ "query-string": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", - "integrity": "sha1-p4wBK3HBfgXy4/ojGd0zBoLvs8s=", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", "requires": { "decode-uri-component": "^0.2.0", "object-assign": "^4.1.0", @@ -2263,7 +2263,7 @@ "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-HBJhu+qhCoBVu8XYq4S3sq/IRqA=" + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, "@types/credit-card-type": { "version": "7.0.0", @@ -12146,7 +12146,7 @@ "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha1-VYqlO0O2YeGSWgr9+japoQhf5Xo=" + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, "lodash.sortby": { "version": "4.7.0", diff --git a/package.json b/package.json index 69abafec19..ba013f3440 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/bigcommerce/checkout-js#readme", "dependencies": { - "@bigcommerce/checkout-sdk": "^1.83.0", + "@bigcommerce/checkout-sdk": "^1.84.0", "@bigcommerce/citadel": "^2.15.1", "@bigcommerce/form-poster": "^1.2.2", "@bigcommerce/memoize": "^1.0.0", diff --git a/src/app/payment/Payment.tsx b/src/app/payment/Payment.tsx index daac057863..75258cde0a 100644 --- a/src/app/payment/Payment.tsx +++ b/src/app/payment/Payment.tsx @@ -247,8 +247,9 @@ class Payment extends Component; } - if (method.id === PaymentMethodId.StripeV3) { + if (method.gateway === PaymentMethodId.StripeV3) { return ; } diff --git a/src/app/payment/paymentMethod/StripePaymentMethod.spec.tsx b/src/app/payment/paymentMethod/StripePaymentMethod.spec.tsx index 7da058dffd..aad8d3977d 100644 --- a/src/app/payment/paymentMethod/StripePaymentMethod.spec.tsx +++ b/src/app/payment/paymentMethod/StripePaymentMethod.spec.tsx @@ -7,21 +7,11 @@ import React, { FunctionComponent } from 'react'; import { CheckoutProvider } from '../../checkout'; import { getStoreConfig } from '../../config/config.mock'; import { createLocaleContext, LocaleContext, LocaleContextType } from '../../locale'; -import { getCreditCardInputStyles } from '../creditCard'; import { getPaymentMethod } from '../payment-methods.mock'; import HostedWidgetPaymentMethod, { HostedWidgetPaymentMethodProps } from './HostedWidgetPaymentMethod'; import { default as PaymentMethodComponent, PaymentMethodProps } from './PaymentMethod'; -jest.mock('../creditCard', () => ({ - ...jest.requireActual('../creditCard'), - getCreditCardInputStyles: jest.fn, Parameters>( - (_containerId, _fieldType) => { - return Promise.resolve({ color: 'rgb(255, 0, 0)', fontWeight: '500', fontFamily: 'Montserrat, Arial, Helvetica, sans-serif', fontSize: '14px', fontSmoothing: 'auto'}); - } - ), -})); - describe('when using Stripe payment', () => { let method: PaymentMethod; let checkoutService: CheckoutService; @@ -39,7 +29,6 @@ describe('when using Stripe payment', () => { checkoutService = createCheckoutService(); checkoutState = checkoutService.getState(); localeContext = createLocaleContext(getStoreConfig()); - method = { ...getPaymentMethod(), id: 'stripev3' }; jest.spyOn(checkoutState.data, 'getConfig') .mockReturnValue(getStoreConfig()); @@ -64,61 +53,134 @@ describe('when using Stripe payment', () => { ); }); - it('renders as hosted widget method', () => { - const container = mount(); - const component: ReactWrapper = container.find(HostedWidgetPaymentMethod); - - expect(component.props()) - .toEqual(expect.objectContaining({ - containerId: 'stripe-card-field', - deinitializePayment: expect.any(Function), - initializePayment: expect.any(Function), - additionalContainerClassName: 'optimizedCheckout-form-input', - method, - })); - }); + describe('when using card component', () => { + beforeEach(() => { + method = { ...getPaymentMethod(), id: 'card', gateway: 'stripev3', method: 'card'}; + }); + + it('renders as hosted widget method', () => { + const container = mount(); + const component: ReactWrapper = container.find(HostedWidgetPaymentMethod); + + expect(component.props()) + .toEqual(expect.objectContaining({ + containerId: `stripe-card-component-field`, + deinitializePayment: expect.any(Function), + initializePayment: expect.any(Function), + additionalContainerClassName: 'optimizedCheckout-form-input widget--stripev3', + method, + })); + }); + + it('initializes method with required config', () => { + const container = mount(); + const component: ReactWrapper = container.find(HostedWidgetPaymentMethod); - it('initializes method with required config', async () => { - const container = mount(); - const component: ReactWrapper = container.find(HostedWidgetPaymentMethod); + component.prop('initializePayment')({ + methodId: method.id, + gatewayId: method.gateway, + }); + + expect(checkoutService.initializePayment) + .toHaveBeenCalledWith(expect.objectContaining({ + methodId: method.id, + stripev3: { + containerId: 'stripe-card-component-field', + options: { + classes: { + base: 'form-input optimizedCheckout-form-input', + }, + }, + }, + })); + }); + }); - component.prop('initializePayment')({ - methodId: method.id, - gatewayId: method.gateway, + describe('when using ideal component', () => { + beforeEach(() => { + method = { ...getPaymentMethod(), id: 'idealBank', gateway: 'stripev3', method: 'idealBank'}; }); - expect(getCreditCardInputStyles) - .toHaveBeenCalledWith('stripe-card-field', ['color', 'fontFamily', 'fontWeight', 'fontSmoothing']); + it('renders as hosted widget method', () => { + const container = mount(); + const component: ReactWrapper = container.find(HostedWidgetPaymentMethod); + + expect(component.props()) + .toEqual(expect.objectContaining({ + containerId: `stripe-idealBank-component-field`, + deinitializePayment: expect.any(Function), + initializePayment: expect.any(Function), + additionalContainerClassName: 'optimizedCheckout-form-input widget--stripev3', + method, + })); + }); - await new Promise(resolve => process.nextTick(resolve)); + it('initializes method with required config', () => { + const container = mount(); + const component: ReactWrapper = container.find(HostedWidgetPaymentMethod); - expect(checkoutService.initializePayment) - .toHaveBeenCalledWith(expect.objectContaining({ + component.prop('initializePayment')({ methodId: method.id, gatewayId: method.gateway, - [method.id]: { - containerId: 'stripe-card-field', - style: { - base: { - color: 'rgb(255, 0, 0)', - fontWeight: '500', - fontFamily: 'Montserrat, Arial, Helvetica, sans-serif', - fontSize: '14px', - fontSmoothing: 'auto', - '::placeholder': { - color: '#E1E1E1', - }, + }); + + expect(checkoutService.initializePayment) + .toHaveBeenCalledWith(expect.objectContaining({ + methodId: method.id, + stripev3: { + containerId: 'stripe-idealBank-component-field', + options: { + classes: { + base: 'form-input optimizedCheckout-form-input', + }, + }, }, - invalid: { - color: 'rgb(255, 0, 0)', - fontWeight: '500', - fontFamily: 'Montserrat, Arial, Helvetica, sans-serif', - fontSize: '14px', - fontSmoothing: 'auto', - iconColor: 'rgb(255, 0, 0)', + }) + ); + }); + }); + + describe('when using iban component', () => { + beforeEach(() => { + method = { ...getPaymentMethod(), id: 'iban', gateway: 'stripev3', method: 'iban'}; + }); + + it('renders as hosted widget method', () => { + const container = mount(); + const component: ReactWrapper = container.find(HostedWidgetPaymentMethod); + + expect(component.props()) + .toEqual(expect.objectContaining({ + containerId: `stripe-iban-component-field`, + deinitializePayment: expect.any(Function), + initializePayment: expect.any(Function), + additionalContainerClassName: 'optimizedCheckout-form-input widget--stripev3', + method, + })); + }); + + it('initializes method with required config', () => { + const container = mount(); + const component: ReactWrapper = container.find(HostedWidgetPaymentMethod); + + component.prop('initializePayment')({ + methodId: method.id, + gatewayId: method.gateway, + }); + + expect(checkoutService.initializePayment) + .toHaveBeenCalledWith(expect.objectContaining({ + methodId: method.id, + stripev3: { + containerId: 'stripe-iban-component-field', + options: { + classes: { + base: 'form-input optimizedCheckout-form-input', + }, + supportedCountries: ['SEPA'], }, }, - }, - })); + })); + }); }); }); diff --git a/src/app/payment/paymentMethod/StripePaymentMethod.tsx b/src/app/payment/paymentMethod/StripePaymentMethod.tsx index a87c17a840..4c753a765c 100644 --- a/src/app/payment/paymentMethod/StripePaymentMethod.tsx +++ b/src/app/payment/paymentMethod/StripePaymentMethod.tsx @@ -1,47 +1,65 @@ -import { PaymentInitializeOptions } from '@bigcommerce/checkout-sdk'; +import { PaymentInitializeOptions, StripeElementOptions } from '@bigcommerce/checkout-sdk'; import React, { useCallback, FunctionComponent } from 'react'; import { Omit } from 'utility-types'; -import { getCreditCardInputStyles, CreditCardInputStylesType } from '../creditCard'; - import HostedWidgetPaymentMethod, { HostedWidgetPaymentMethodProps } from './HostedWidgetPaymentMethod'; -export type SquarePaymentMethodProps = Omit; +export type StripePaymentMethodProps = Omit; + +export interface StripeOptions { + card: StripeElementOptions; + iban: StripeElementOptions; + idealBank: StripeElementOptions; +} -const StripePaymentMethod: FunctionComponent = ({ +export enum StripeV3PaymentMethodType { + card = 'card', + iban = 'iban', + idealBank = 'idealBank', +} + +const StripePaymentMethod: FunctionComponent = ({ initializePayment, + method, ...rest }) => { + const paymentMethodType = method.id as StripeV3PaymentMethodType; + const containerId = `stripe-${paymentMethodType}-component-field`; + const initializeStripePayment = useCallback(async (options: PaymentInitializeOptions) => { - const creditCardInputStyles = await getCreditCardInputStyles('stripe-card-field', ['color', 'fontFamily', 'fontWeight', 'fontSmoothing']); - const creditCardInputErrorStyles = await getCreditCardInputStyles('stripe-card-field', ['color'], CreditCardInputStylesType.Error); + const classes = { + base: 'form-input optimizedCheckout-form-input', + }; + + const stripeOptions: StripeOptions = { + [StripeV3PaymentMethodType.card]: { + classes, + }, + [StripeV3PaymentMethodType.iban]: { + ...{ classes }, + supportedCountries: ['SEPA'], + }, + [StripeV3PaymentMethodType.idealBank]: { + classes, + }, + }; return initializePayment({ ...options, stripev3: { - containerId: 'stripe-card-field', - style: { - base: { - ...creditCardInputStyles, - '::placeholder': { - color: '#E1E1E1', - }, - }, - invalid: { - ...creditCardInputErrorStyles, - iconColor: creditCardInputErrorStyles.color, - }, - }, + containerId, + options: stripeOptions[paymentMethodType], }, }); - }, [initializePayment]); + }, [initializePayment, containerId, paymentMethodType]); return ; }; diff --git a/src/scss/components/checkout/widget/_widget.scss b/src/scss/components/checkout/widget/_widget.scss index 73ebf359a5..161def147f 100644 --- a/src/scss/components/checkout/widget/_widget.scss +++ b/src/scss/components/checkout/widget/_widget.scss @@ -59,6 +59,7 @@ margin-bottom: remCalc(20px); margin-top: remCalc(20px); + padding: 1rem; } .StripeElement--focus {