Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

New master #874

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b2de60b
docs: updating readme (#1)
christopappas Apr 5, 2024
bdb2358
refactor: Add shipping address for Afterpay, PaymentIntentUnexpectedS…
julianajlk Feb 9, 2024
3f144a9
feat: Add DynamicPaymentMethodsNotCompatibleError to handle BNPL DPM …
julianajlk Mar 4, 2024
a25976a
refactor: Postal code required for BNPL DPM supported countries since…
julianajlk Mar 4, 2024
4529662
fix: Update stripe checkout function
julianajlk Mar 4, 2024
a8dddd2
refactor: Get paymentMethodType onChange and send shipping address on…
julianajlk Mar 21, 2024
7d2222c
feat: Add PaymentMethodMessagingElement, modify currencySelector, use…
julianajlk Mar 21, 2024
9c00e5f
refactor: Modify checkout function to handleNextAction for DPM payments
julianajlk Mar 25, 2024
eaaed95
fix: Add isPaymentRedirect to selectors for redirect to receipt
julianajlk Mar 25, 2024
a641b79
refactor: Modify PaymentPage to account for redirect from DPM and red…
julianajlk Mar 23, 2024
ee959b4
refactor: Use stripe promise from PaymentPage parent in Checkout
julianajlk Mar 23, 2024
9b81cc0
fix: Update invalid_request_error to include error in missing state a…
julianajlk Mar 29, 2024
fd2bccc
fix: Only Klarna has redirect_status on return_url, cannot rely on it
julianajlk Mar 29, 2024
0a8c584
fix: Remove no longer needed PaymentIntentUnexpectedStateError, handl…
julianajlk Mar 29, 2024
521c606
fix: Reorder error type in handleRequestError from feedback
julianajlk Apr 9, 2024
e1b548d
test: Add more tests
julianajlk Apr 9, 2024
b60c260
fix: Add dpm_enabled query param to receipt URL
julianajlk Apr 10, 2024
7fa34ad
fix: Add stripeSelectedPaymentMethod to payment_selected click event
julianajlk Apr 15, 2024
85e5f51
Merge pull request #4 from edx/julianajlk/REV-3830/stripe-dpm
julianajlk Apr 17, 2024
d71837f
fix: Add country compatibility with BNPL Affirm at the form level (#5)
julianajlk Apr 23, 2024
83a63fc
refactor: DPM loading lag for successful payments redirect (#6)
julianajlk Apr 26, 2024
2c35546
chore: Clean up allowlist follow-redirects and webpack-dev-middleware…
julianajlk May 2, 2024
0de1892
refactor: Reorder DPM successful loading redirect (#8)
julianajlk May 2, 2024
bc3becb
fix: Remove DPM Canada form validation (#10)
julianajlk May 6, 2024
4aeb2f5
feat: Add PageLoadingDynamicPaymentMethods with timeout for DPM recei…
julianajlk May 16, 2024
5a61584
feat: remove cybersource script (#7)
christopappas May 28, 2024
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
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ REFRESH_ACCESS_TOKEN_ENDPOINT=null
SEGMENT_KEY=null
SITE_NAME=null
USER_INFO_COOKIE_NAME=null
USER_LOCATION_COOKIE_NAME=null
LOCATION_OVERRIDE_COOKIE=null
CURRENCY_COOKIE_NAME=null
SUPPORT_URL=null
CYBERSOURCE_URL=null
Expand Down
2 changes: 2 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
SEGMENT_KEY='VMsX2obE9Xveo4se3c6CmfdG0LZVc7qI'
SITE_NAME='edX'
USER_INFO_COOKIE_NAME='edx-user-info'
USER_LOCATION_COOKIE_NAME='prod-edx-cf-loc'
LOCATION_OVERRIDE_COOKIE='location-override'
CURRENCY_COOKIE_NAME='edx-price-l10n'
SUPPORT_URL='http://localhost:18000/support'
CYBERSOURCE_URL='https://testsecureacceptance.cybersource.com/silent/pay'
Expand Down
2 changes: 2 additions & 0 deletions .env.development-stage
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ REFRESH_ACCESS_TOKEN_ENDPOINT='/proxy/lms/login_refresh'
SEGMENT_KEY=null
SITE_NAME='edX'
USER_INFO_COOKIE_NAME='npmstage-edx-user-info'
USER_LOCATION_COOKIE_NAME='prod-edx-cf-loc'
LOCATION_OVERRIDE_COOKIE='location-override'
CURRENCY_COOKIE_NAME='edx-price-l10n'
SUPPORT_URL='/proxy/lms/support'
CYBERSOURCE_URL='https://testsecureacceptance.cybersource.com/silent/pay'
Expand Down
2 changes: 2 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
SEGMENT_KEY=null
SITE_NAME='edX'
USER_INFO_COOKIE_NAME='edx-user-info'
USER_LOCATION_COOKIE_NAME='prod-edx-cf-loc'
LOCATION_OVERRIDE_COOKIE='location-override'

# App specific
CURRENCY_COOKIE_NAME='edx-price-l10n'
Expand Down
4 changes: 2 additions & 2 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# PCI compliance requires stricter scrutiny, see PR template
/src @openedx/revenue-squad
CODEOWNERS @openedx/revenue-squad
/src @edx/revenue-squad
CODEOWNERS @edx/revenue-squad
4 changes: 3 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
frontend-app-payment
====================

Please tag **@edx/revenue-squad** on any PRs or issues. Thanks.
Please tag **@edx/revenue-squad** on any PRs or issues. Thanks!

This repo is intended for the use of 2U employees.

Introduction
------------
Expand Down
3 changes: 0 additions & 3 deletions audit-ci.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
{
"allowlist": [
"GHSA-hpx4-r86g-5jrg",
"GHSA-wf5p-g6vw-rhxx",
"GHSA-cxjh-pqwp-8mfp",
"GHSA-wr3j-pwj9-hqq6",
"GHSA-rv95-896h-c2vc"
],
"moderate": true
Expand Down
1 change: 1 addition & 0 deletions docs/how_tos/feedback.rst
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ pre-built APIs that do not follow the format of the feedback module in the:
* `feedback/data/sagas.js`_

* ``basket-changed-error-message``
* ``dynamic-payment-methods-country-not-compatible``
* ``transaction-declined-message``
* ``error_message`` in URL parameters

Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@
</div>
</div>

<script type="text/javascript" src="https://flex.cybersource.com/cybersource/assets/microform/0.11/flex-microform.min.js"></script>
</body>
</html>
4 changes: 4 additions & 0 deletions src/components/formatted-alert-list/FormattedAlertList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
EnrollmentCodeQuantityUpdated,
TransactionDeclined,
BasketChangedError,
DynamicPaymentMethodsNotCompatibleError,
CaptureKeyTimeoutTwoMinutes,
CaptureKeyTimeoutOneMinute,
} from '../../payment/AlertCodeMessages';
Expand Down Expand Up @@ -47,6 +48,9 @@ export const FormattedAlertList = (props) => {
'basket-changed-error-message': (
<BasketChangedError />
),
'dynamic-payment-methods-country-not-compatible': (
<DynamicPaymentMethodsNotCompatibleError />
),
'capture-key-2mins-message': (
<CaptureKeyTimeoutTwoMinutes />
),
Expand Down
2 changes: 1 addition & 1 deletion src/feedback/data/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function* handleErrors(e, clearExistingMessages) {
if (e.errors !== undefined) {
for (let i = 0; i < e.errors.length; i++) { // eslint-disable-line no-plusplus
const error = e.errors[i];
if (error.code === 'basket-changed-error-message') {
if (error.code === 'basket-changed-error-message' || error.code === 'dynamic-payment-methods-country-not-compatible') {
yield put(addMessage(error.code, error.userMessage, {}, MESSAGE_TYPES.ERROR));
} else if (error.data === undefined && error.messageType === null) {
yield put(addMessage('transaction-declined-message', error.userMessage, {}, MESSAGE_TYPES.ERROR));
Expand Down
8 changes: 8 additions & 0 deletions src/payment/AlertCodeMessages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ export const TransactionDeclined = () => (
/>
);

export const DynamicPaymentMethodsNotCompatibleError = () => (
<FormattedMessage
id="payment.messages.transaction.error.dynamic_payment_methods_not_compatible"
defaultMessage="The payment method you have selected is not available in your country. Please select another payment method."
description="Notifies the user their billing country is not compatible with the Dynamic Payment Method selected."
/>
);

export const BasketChangedError = () => (
<FormattedMessage
id="payment.messages.transaction.error.basket_changed"
Expand Down
26 changes: 26 additions & 0 deletions src/payment/AlertCodeMessages.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
SingleEnrollmentCodeWarning,
EnrollmentCodeQuantityUpdated,
TransactionDeclined,
DynamicPaymentMethodsNotCompatibleError,
BasketChangedError,
} from './AlertCodeMessages';

const mockStore = configureMockStore();
Expand Down Expand Up @@ -51,3 +53,27 @@ describe('TransactionDeclined', () => {
expect(tree).toMatchSnapshot();
});
});

describe('DynamicPaymentMethodsNotCompatibleError', () => {
it('should render with values', () => {
const component = (
<IntlProvider locale="en">
<DynamicPaymentMethodsNotCompatibleError />
</IntlProvider>
);
const { container: tree } = render(component);
expect(tree).toMatchSnapshot();
});
});

describe('BasketChangedError', () => {
it('should render with values', () => {
const component = (
<IntlProvider locale="en">
<BasketChangedError />
</IntlProvider>
);
const { container: tree } = render(component);
expect(tree).toMatchSnapshot();
});
});
58 changes: 58 additions & 0 deletions src/payment/PageLoadingDynamicPaymentMethods.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
import { logInfo } from '@edx/frontend-platform/logging';

const PageLoadingDynamicPaymentMethods = ({ srMessage, orderNumber }) => {
useEffect(() => {
const timer = setTimeout(() => {
logInfo(`Dynamic Payment Methods payment succeeded for edX order number ${orderNumber}, redirecting to ecommerce receipt page.`);
const queryParams = `order_number=${orderNumber}&disable_back_button=${Number(true)}&dpm_enabled=${true}`;

if (getConfig().ENVIRONMENT !== 'test') {
/* istanbul ignore next */
global.location.assign(`${getConfig().ECOMMERCE_BASE_URL}/checkout/receipt/?${queryParams}`);
}
}, 3000); // Delay the redirect to receipt page by 3 seconds to make sure ecomm order fulfillment is done.

return () => clearTimeout(timer); // On unmount, clear the timer
}, [srMessage, orderNumber]);

const renderSrMessage = () => {
if (!srMessage) {
return null;
}

return (
<span className="sr-only">
{srMessage}
</span>
);
};

return (
<div>
<div
className="d-flex justify-content-center align-items-center flex-column"
style={{
height: '50vh',
}}
>
<div className="spinner-border text-primary" data-testid="loading-page" role="status">
{renderSrMessage()}
</div>
</div>
</div>
);
};

PageLoadingDynamicPaymentMethods.propTypes = {
srMessage: PropTypes.string.isRequired,
orderNumber: PropTypes.string,
};

PageLoadingDynamicPaymentMethods.defaultProps = {
orderNumber: null,
};

export default PageLoadingDynamicPaymentMethods;
77 changes: 77 additions & 0 deletions src/payment/PageLoadingDynamicPaymentMethods.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { render, act } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { logInfo } from '@edx/frontend-platform/logging';

import createRootReducer from '../data/reducers';
import PageLoadingDynamicPaymentMethods from './PageLoadingDynamicPaymentMethods';

jest.mock('@edx/frontend-platform/logging', () => ({
logInfo: jest.fn(),
}));

describe('PageLoadingDynamicPaymentMethods', () => {
let store;

beforeEach(() => {
store = createStore(createRootReducer());
jest.useFakeTimers();
jest.clearAllMocks();
});

afterEach(() => {
jest.runOnlyPendingTimers();
jest.useRealTimers();
});

it('renders <PageLoadingDynamicPaymentMethods />', () => {
const component = (
<IntlProvider locale="en">
<Provider store={store}>
<PageLoadingDynamicPaymentMethods
srMessage=""
orderNumber="EDX-100001"
/>
</Provider>
</IntlProvider>
);
const { container: tree } = render(component);
expect(tree).toMatchSnapshot();
});

it('it redirects to receipt page after 3 seconds delay', () => {
const orderNumber = 'EDX-100001';
const logMessage = `Dynamic Payment Methods payment succeeded for edX order number ${orderNumber}, redirecting to ecommerce receipt page.`;
render(
<IntlProvider locale="en">
<Provider store={store}>
<PageLoadingDynamicPaymentMethods
srMessage=""
orderNumber={orderNumber}
/>
</Provider>
</IntlProvider>,
);

act(() => {
jest.advanceTimersByTime(3000);
});
expect(logInfo).toHaveBeenCalledWith(expect.stringMatching(logMessage));
});

it('cleans up the timer on unmount', () => {
const { unmount } = render(
<PageLoadingDynamicPaymentMethods
srMessage=""
orderNumber="EDX-100001"
/>,
);
unmount();
act(() => {
jest.advanceTimersByTime(3000);
});
expect(logInfo).not.toHaveBeenCalled();
});
});
Loading