@edx/frontend-app-alacarte-checkout 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CartSummary/CartSummary.d.ts +6 -3
- package/dist/components/CartSummary/CartSummary.js +3 -3
- package/dist/components/CartSummary/CartSummary.js.map +1 -1
- package/dist/components/Checkout/Checkout.js +22 -12
- package/dist/components/Checkout/Checkout.js.map +1 -1
- package/dist/components/Checkout/Messages.d.ts +25 -0
- package/dist/components/Checkout/Messages.js +25 -0
- package/dist/components/Checkout/Messages.js.map +1 -1
- package/dist/components/PaymentFields/PaymentFields.d.ts +7 -1
- package/dist/components/PaymentFields/PaymentFields.js +6 -1
- package/dist/components/PaymentFields/PaymentFields.js.map +1 -1
- package/dist/components/PaymentForm/Messages.d.ts +5 -0
- package/dist/components/PaymentForm/Messages.js +5 -0
- package/dist/components/PaymentForm/Messages.js.map +1 -1
- package/dist/components/PaymentForm/PaymentForm.d.ts +6 -2
- package/dist/components/PaymentForm/PaymentForm.js +42 -5
- package/dist/components/PaymentForm/PaymentForm.js.map +1 -1
- package/dist/components/PromoCode/PromoCode.js +4 -2
- package/dist/components/PromoCode/PromoCode.js.map +1 -1
- package/dist/data/hooks/useAlacarteCheckout.d.ts +7 -1
- package/dist/data/hooks/useAlacarteCheckout.js +112 -40
- package/dist/data/hooks/useAlacarteCheckout.js.map +1 -1
- package/dist/data/hooks/useCheckout.d.ts +3 -1
- package/dist/data/hooks/useCheckout.js +43 -1
- package/dist/data/hooks/useCheckout.js.map +1 -1
- package/dist/styles/components/checkout.scss +29 -1
- package/dist/styles/components/paymentFields.scss +36 -0
- package/dist/styles/components/paymentForm.scss +6 -0
- package/dist/styles/components/programEnrollmentCard.scss +13 -0
- package/dist/styles/components/promoCode.scss +76 -6
- package/dist/types/alacarte.d.ts +9 -0
- package/dist/types/alacarte.js.map +1 -1
- package/dist/types.d.ts +15 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/provision.js +2 -2
- package/dist/utils/provision.js.map +1 -1
- package/package.json +1 -1
|
@@ -25,11 +25,14 @@ export interface CartSummaryProps {
|
|
|
25
25
|
/** À la carte checkout line — when present, drives the subtotal/total and
|
|
26
26
|
* the product name shown, in place of the generic edX Plus placeholder. */
|
|
27
27
|
alacarteLine?: AlacarteLine | null;
|
|
28
|
-
/** Program catalog detail (filterTwouProgram) — when
|
|
29
|
-
*
|
|
28
|
+
/** Program catalog detail (filterTwouProgram) — when present, drives the
|
|
29
|
+
* "Courses included in this program" list. */
|
|
30
30
|
programDetail?: ProgramDetail | null;
|
|
31
|
+
/** Fallback program name — used when there is no alacarteLine to provide it
|
|
32
|
+
* (e.g. already-owned overlay). */
|
|
33
|
+
programName?: string | null;
|
|
31
34
|
/** Coupon input — when set, wires PromoCode to tCheckoutUpdate. */
|
|
32
35
|
promoCode?: PromoCodeProps | null;
|
|
33
36
|
}
|
|
34
|
-
declare const CartSummary: ({ productTitle, productBlurb, learnerCount: learnerCountProp, course, alacarteLine, programDetail, promoCode, }: CartSummaryProps) => import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
declare const CartSummary: ({ productTitle, productBlurb, learnerCount: learnerCountProp, course, alacarteLine, programDetail, programName: programNameProp, promoCode, }: CartSummaryProps) => import("react/jsx-runtime").JSX.Element;
|
|
35
38
|
export default CartSummary;
|
|
@@ -9,7 +9,7 @@ import PromoCode from '../PromoCode/PromoCode';
|
|
|
9
9
|
import messages from './Messages';
|
|
10
10
|
import learnersIcon from './world-icon.svg';
|
|
11
11
|
import institutionsIcon from './student-icon.svg';
|
|
12
|
-
const CartSummary = ({ productTitle, productBlurb, learnerCount: learnerCountProp, course = null, alacarteLine = null, programDetail = null, promoCode = null, }) => {
|
|
12
|
+
const CartSummary = ({ productTitle, productBlurb, learnerCount: learnerCountProp, course = null, alacarteLine = null, programDetail = null, programName: programNameProp = null, promoCode = null, }) => {
|
|
13
13
|
var _a, _b, _c, _d;
|
|
14
14
|
const { formatMessage } = useIntl();
|
|
15
15
|
const title = productTitle !== null && productTitle !== void 0 ? productTitle : formatMessage(messages.productTitle);
|
|
@@ -33,7 +33,7 @@ const CartSummary = ({ productTitle, productBlurb, learnerCount: learnerCountPro
|
|
|
33
33
|
: '';
|
|
34
34
|
const lineName = alacarteLine
|
|
35
35
|
? (alacarteLine.kind === 'course' ? alacarteLine.courseName : alacarteLine.programName)
|
|
36
|
-
:
|
|
36
|
+
: programNameProp;
|
|
37
37
|
const ownedCourses = isProgramLine && alacarteLine.kind === 'program'
|
|
38
38
|
? ((_c = alacarteLine.ownedCourses) !== null && _c !== void 0 ? _c : [])
|
|
39
39
|
: [];
|
|
@@ -47,7 +47,7 @@ const CartSummary = ({ productTitle, productBlurb, learnerCount: learnerCountPro
|
|
|
47
47
|
: null;
|
|
48
48
|
return (_jsxs("div", { className: "cart-summary ", children: [_jsxs(Card, { className: "cart-summary__card p-3", children: [_jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center p-0 pb-3", children: [_jsx("span", { className: "font-weight-bold text-gray-900", children: formatMessage(messages.summaryHeading) }), !isProgramLine && (_jsxs(Badge, { variant: "light", className: "d-flex align-items-center px-2 py-1", children: [_jsx(Icon, { src: Group, className: "mr-1", style: { width: '1rem', height: '1rem' } }), subscriberCount == null ? (_jsx("span", { className: "cart-summary__count-skeleton", "aria-busy": "true", "aria-label": formatMessage(messages.loadingSubscribers), "data-testid": "subscriber-count-skeleton", children: _jsx(Skeleton, { width: 96, height: 12 }) })) : (formatMessage(messages.learnersSubscribed, {
|
|
49
49
|
count: subscriberCount.toLocaleString(),
|
|
50
|
-
}))] }))] }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "p-0 py-3", children: [course ? (_jsx(CourseEnrollmentCard, { course: course, variant: "summary" })) :
|
|
50
|
+
}))] }))] }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "p-0 py-3", children: [course ? (_jsx(CourseEnrollmentCard, { course: course, variant: "summary" })) : programDetail ? (_jsx(ProgramEnrollmentCard, { program: programDetail, programName: lineName !== null && lineName !== void 0 ? lineName : '' })) : lineName ? (_jsx("p", { className: "font-weight-bold mb-0 text-primary-500", children: lineName })) : (_jsxs("div", { className: "d-flex align-items-start", children: [_jsx(EdxUnlimitedLogo, { ariaLabel: "edX Plus" }), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0 text-primary-500", children: title }), _jsx("p", { className: "small mb-0 text-gray-900", children: blurb })] })] })), ownedCourseNames && (_jsx("p", { className: "cart-summary__already-yours small text-gray-700 mb-0 mt-2", "data-testid": "already-yours", children: formatMessage(messages.alreadyYours, { names: ownedCourseNames }) }))] }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "cart-summary__subtotal-row d-flex justify-content-between align-items-center p-0 pt-3", children: [_jsx("span", { className: "cart-summary__subtotal text-gray-900", children: formatMessage(messages.subtotal) }), _jsx("span", { className: "cart-summary__subtotal text-gray-900", children: subtotalText })] }), deduction && (_jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center p-0", "data-testid": "deduction-row", children: [_jsx("span", { className: "text-gray-700", children: formatMessage(messages.deduction) }), _jsx("span", { className: "text-gray-700", children: formatMessage(messages.deductionAmount, {
|
|
51
51
|
amount: deduction.amount.toFixed(2),
|
|
52
52
|
currency: deduction.currency,
|
|
53
53
|
}) })] })), discount && (_jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center p-0", "data-testid": "discount-row", children: [_jsx("span", { className: "text-success font-weight-bold", children: formatMessage(messages.discountApplied) }), _jsx("span", { className: "text-success font-weight-bold", children: formatMessage(messages.discountAmount, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CartSummary.js","sourceRoot":"","sources":["../../../src/components/CartSummary/CartSummary.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport { Badge, Card, Icon, Skeleton } from '@openedx/paragon';\nimport { Group } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { EdxUnlimitedLogo } from '../BrandMarks/BrandMarks';\nimport CourseEnrollmentCard from '../CourseEnrollmentCard/CourseEnrollmentCard';\nimport ProgramEnrollmentCard from '../ProgramEnrollmentCard/ProgramEnrollmentCard';\nimport PromoCode from '../PromoCode/PromoCode';\nimport type { PromoCodeProps } from '../PromoCode/PromoCode';\nimport type { CourseDetail } from '../../types';\nimport type { AlacarteLine, ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\nimport learnersIcon from './world-icon.svg';\nimport institutionsIcon from './student-icon.svg';\n\nexport type BillingCycle = 'monthly' | 'annual';\n\nexport interface CartSummaryProps {\n productTitle?: string,\n productBlurb?: string,\n /** Promotional monthly price amount e.g. \"39\" */\n monthlyAmount?: string,\n /** Regular monthly price after the promo period e.g. \"59\" */\n monthlyRegularAmount?: string,\n /** Length of the promotional period in months */\n promoMonths?: number,\n /** Annual price amount e.g. \"299\" */\n annualAmount?: string,\n currency?: string,\n defaultCycle?: BillingCycle,\n monthlyRenewalLabel?: string,\n annualRenewalLabel?: string,\n /** Called when the learner switches monthly ↔ annual. */\n onCycleChange?: (cycle: BillingCycle) => void,\n learnerCount?: number,\n /** When set, shows the course enrollment preview card below the summary card. */\n course?: CourseDetail | null,\n /** À la carte checkout line — when present, drives the subtotal/total and\n * the product name shown, in place of the generic edX Plus placeholder. */\n alacarteLine?: AlacarteLine | null,\n /** Program catalog detail (filterTwouProgram) — when the line is a program,\n * drives the \"Courses included in this program\" list. */\n programDetail?: ProgramDetail | null,\n /** Coupon input — when set, wires PromoCode to tCheckoutUpdate. */\n promoCode?: PromoCodeProps | null,\n}\n\nconst CartSummary = ({\n productTitle,\n productBlurb,\n learnerCount: learnerCountProp,\n course = null,\n alacarteLine = null,\n programDetail = null,\n promoCode = null,\n}: CartSummaryProps) => {\n const { formatMessage } = useIntl();\n const title = productTitle ?? formatMessage(messages.productTitle);\n const blurb = productBlurb ?? formatMessage(messages.productBlurb);\n\n // Prefer the course's own enrollment count. Programs have no equivalent field\n // — the badge is hidden for those rather than inventing a figure.\n const isProgramLine = alacarteLine?.kind === 'program';\n const subscriberCount = course?.tEnrollmentCount ?? learnerCountProp ?? null;\n\n // No fallback amount — an unresolved à la carte line means we don't know\n // the price yet, so the row is left blank rather than showing a made-up\n // number that could be mistaken for the real one.\n const subtotalText = alacarteLine?.price\n ? formatMessage(messages.subtotalAmount, {\n amount: alacarteLine.price.amount.toFixed(2), currency: alacarteLine.price.currency,\n })\n : '';\n const totalText = alacarteLine?.amountDue\n ? formatMessage(messages.todaysTotalValue, {\n amount: alacarteLine.amountDue.amount.toFixed(2), currency: alacarteLine.amountDue.currency,\n })\n : '';\n\n const lineName = alacarteLine\n ? (alacarteLine.kind === 'course' ? alacarteLine.courseName : alacarteLine.programName)\n : null;\n\n const ownedCourses = isProgramLine && alacarteLine.kind === 'program'\n ? (alacarteLine.ownedCourses ?? [])\n : [];\n const ownedCourseNames = ownedCourses\n .map((c) => c.name)\n .filter(Boolean)\n .join(', ');\n const discount = alacarteLine?.discount ?? null;\n\n const deduction = isProgramLine && alacarteLine.kind === 'program'\n ? alacarteLine.deduction\n : null;\n\n return (\n <div className=\"cart-summary \">\n <Card className=\"cart-summary__card p-3\">\n <Card.Section className=\"d-flex justify-content-between align-items-center p-0 pb-3\">\n <span className=\"font-weight-bold text-gray-900\">\n {formatMessage(messages.summaryHeading)}\n </span>\n {!isProgramLine && (\n <Badge\n variant=\"light\"\n className=\"d-flex align-items-center px-2 py-1\"\n >\n <Icon\n src={Group}\n className=\"mr-1\"\n style={{ width: '1rem', height: '1rem' }}\n />\n {subscriberCount == null ? (\n <span\n className=\"cart-summary__count-skeleton\"\n aria-busy=\"true\"\n aria-label={formatMessage(messages.loadingSubscribers)}\n data-testid=\"subscriber-count-skeleton\"\n >\n <Skeleton width={96} height={12} />\n </span>\n ) : (\n formatMessage(messages.learnersSubscribed, {\n count: subscriberCount.toLocaleString(),\n })\n )}\n </Badge>\n )}\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"p-0 py-3\">\n {course ? (\n <CourseEnrollmentCard course={course} variant=\"summary\" />\n ) : isProgramLine && programDetail ? (\n <ProgramEnrollmentCard program={programDetail} programName={lineName ?? ''} />\n ) : lineName ? (\n <p className=\"font-weight-bold mb-0 text-primary-500\">{lineName}</p>\n ) : (\n <div className=\"d-flex align-items-start\">\n <EdxUnlimitedLogo ariaLabel=\"edX Plus\" />\n <div>\n <p className=\"font-weight-bold mb-0 text-primary-500\">{title}</p>\n <p className=\"small mb-0 text-gray-900\">{blurb}</p>\n </div>\n </div>\n )}\n {ownedCourseNames && (\n <p\n className=\"cart-summary__already-yours small text-gray-700 mb-0 mt-2\"\n data-testid=\"already-yours\"\n >\n {formatMessage(messages.alreadyYours, { names: ownedCourseNames })}\n </p>\n )}\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"cart-summary__subtotal-row d-flex justify-content-between align-items-center p-0 pt-3\">\n <span className=\"cart-summary__subtotal text-gray-900\">\n {formatMessage(messages.subtotal)}\n </span>\n <span className=\"cart-summary__subtotal text-gray-900\">\n {subtotalText}\n </span>\n </Card.Section>\n\n {deduction && (\n <Card.Section\n className=\"d-flex justify-content-between align-items-center p-0\"\n data-testid=\"deduction-row\"\n >\n <span className=\"text-gray-700\">\n {formatMessage(messages.deduction)}\n </span>\n <span className=\"text-gray-700\">\n {formatMessage(messages.deductionAmount, {\n amount: deduction.amount.toFixed(2),\n currency: deduction.currency,\n })}\n </span>\n </Card.Section>\n )}\n\n {discount && (\n <Card.Section\n className=\"d-flex justify-content-between align-items-center p-0\"\n data-testid=\"discount-row\"\n >\n <span className=\"text-success font-weight-bold\">\n {formatMessage(messages.discountApplied)}\n </span>\n <span className=\"text-success font-weight-bold\">\n {formatMessage(messages.discountAmount, {\n amount: discount.amount.toFixed(2),\n currency: discount.currency,\n })}\n </span>\n </Card.Section>\n )}\n\n <Card.Section className=\"d-flex justify-content-between align-items-center p-0\">\n <span className=\"cart-summary__total-label text-gray-900\">\n {formatMessage(messages.todaysTotal)}\n </span>\n <span className=\"cart-summary__total-value mb-0 text-gray-900\">\n {totalText}\n </span>\n </Card.Section>\n </Card>\n\n {promoCode ? <PromoCode {...promoCode} /> : null}\n\n {/* Stats row */}\n <div className=\"cart-summary__stats d-none d-lg-flex\">\n <div className=\"cart-summary__stat d-flex align-items-center mr-4\">\n <img src={learnersIcon} alt=\"\" width={50} height={50} className=\"mr-2\" />\n <div>\n <p className=\"cart-summary__stat-count font-weight-bold text-gray-500 mb-0\">\n {formatMessage(messages.learnersCount)}\n </p>\n <p className=\"cart-summary__stat-label text-gray-500 mb-0\">\n {formatMessage(messages.learnersLabel)}\n </p>\n </div>\n </div>\n <div className=\"cart-summary__stat d-flex align-items-center\">\n <img src={institutionsIcon} alt=\"\" width={50} height={50} className=\"mr-2\" />\n <div>\n <p className=\"cart-summary__stat-count font-weight-bold text-gray-500 mb-0\">\n {formatMessage(messages.institutionsCount)}\n </p>\n <p className=\"cart-summary__stat-label text-gray-500 mb-0\">\n {formatMessage(messages.institutionsLabel)}\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default CartSummary;\n"]}
|
|
1
|
+
{"version":3,"file":"CartSummary.js","sourceRoot":"","sources":["../../../src/components/CartSummary/CartSummary.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport { Badge, Card, Icon, Skeleton } from '@openedx/paragon';\nimport { Group } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { EdxUnlimitedLogo } from '../BrandMarks/BrandMarks';\nimport CourseEnrollmentCard from '../CourseEnrollmentCard/CourseEnrollmentCard';\nimport ProgramEnrollmentCard from '../ProgramEnrollmentCard/ProgramEnrollmentCard';\nimport PromoCode from '../PromoCode/PromoCode';\nimport type { PromoCodeProps } from '../PromoCode/PromoCode';\nimport type { CourseDetail } from '../../types';\nimport type { AlacarteLine, ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\nimport learnersIcon from './world-icon.svg';\nimport institutionsIcon from './student-icon.svg';\n\nexport type BillingCycle = 'monthly' | 'annual';\n\nexport interface CartSummaryProps {\n productTitle?: string,\n productBlurb?: string,\n /** Promotional monthly price amount e.g. \"39\" */\n monthlyAmount?: string,\n /** Regular monthly price after the promo period e.g. \"59\" */\n monthlyRegularAmount?: string,\n /** Length of the promotional period in months */\n promoMonths?: number,\n /** Annual price amount e.g. \"299\" */\n annualAmount?: string,\n currency?: string,\n defaultCycle?: BillingCycle,\n monthlyRenewalLabel?: string,\n annualRenewalLabel?: string,\n /** Called when the learner switches monthly ↔ annual. */\n onCycleChange?: (cycle: BillingCycle) => void,\n learnerCount?: number,\n /** When set, shows the course enrollment preview card below the summary card. */\n course?: CourseDetail | null,\n /** À la carte checkout line — when present, drives the subtotal/total and\n * the product name shown, in place of the generic edX Plus placeholder. */\n alacarteLine?: AlacarteLine | null,\n /** Program catalog detail (filterTwouProgram) — when present, drives the\n * \"Courses included in this program\" list. */\n programDetail?: ProgramDetail | null,\n /** Fallback program name — used when there is no alacarteLine to provide it\n * (e.g. already-owned overlay). */\n programName?: string | null,\n /** Coupon input — when set, wires PromoCode to tCheckoutUpdate. */\n promoCode?: PromoCodeProps | null,\n}\n\nconst CartSummary = ({\n productTitle,\n productBlurb,\n learnerCount: learnerCountProp,\n course = null,\n alacarteLine = null,\n programDetail = null,\n programName: programNameProp = null,\n promoCode = null,\n}: CartSummaryProps) => {\n const { formatMessage } = useIntl();\n const title = productTitle ?? formatMessage(messages.productTitle);\n const blurb = productBlurb ?? formatMessage(messages.productBlurb);\n\n // Prefer the course's own enrollment count. Programs have no equivalent field\n // — the badge is hidden for those rather than inventing a figure.\n const isProgramLine = alacarteLine?.kind === 'program';\n const subscriberCount = course?.tEnrollmentCount ?? learnerCountProp ?? null;\n\n // No fallback amount — an unresolved à la carte line means we don't know\n // the price yet, so the row is left blank rather than showing a made-up\n // number that could be mistaken for the real one.\n const subtotalText = alacarteLine?.price\n ? formatMessage(messages.subtotalAmount, {\n amount: alacarteLine.price.amount.toFixed(2), currency: alacarteLine.price.currency,\n })\n : '';\n const totalText = alacarteLine?.amountDue\n ? formatMessage(messages.todaysTotalValue, {\n amount: alacarteLine.amountDue.amount.toFixed(2), currency: alacarteLine.amountDue.currency,\n })\n : '';\n\n const lineName = alacarteLine\n ? (alacarteLine.kind === 'course' ? alacarteLine.courseName : alacarteLine.programName)\n : programNameProp;\n\n const ownedCourses = isProgramLine && alacarteLine.kind === 'program'\n ? (alacarteLine.ownedCourses ?? [])\n : [];\n const ownedCourseNames = ownedCourses\n .map((c) => c.name)\n .filter(Boolean)\n .join(', ');\n const discount = alacarteLine?.discount ?? null;\n\n const deduction = isProgramLine && alacarteLine.kind === 'program'\n ? alacarteLine.deduction\n : null;\n\n return (\n <div className=\"cart-summary \">\n <Card className=\"cart-summary__card p-3\">\n <Card.Section className=\"d-flex justify-content-between align-items-center p-0 pb-3\">\n <span className=\"font-weight-bold text-gray-900\">\n {formatMessage(messages.summaryHeading)}\n </span>\n {!isProgramLine && (\n <Badge\n variant=\"light\"\n className=\"d-flex align-items-center px-2 py-1\"\n >\n <Icon\n src={Group}\n className=\"mr-1\"\n style={{ width: '1rem', height: '1rem' }}\n />\n {subscriberCount == null ? (\n <span\n className=\"cart-summary__count-skeleton\"\n aria-busy=\"true\"\n aria-label={formatMessage(messages.loadingSubscribers)}\n data-testid=\"subscriber-count-skeleton\"\n >\n <Skeleton width={96} height={12} />\n </span>\n ) : (\n formatMessage(messages.learnersSubscribed, {\n count: subscriberCount.toLocaleString(),\n })\n )}\n </Badge>\n )}\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"p-0 py-3\">\n {course ? (\n <CourseEnrollmentCard course={course} variant=\"summary\" />\n ) : programDetail ? (\n <ProgramEnrollmentCard program={programDetail} programName={lineName ?? ''} />\n ) : lineName ? (\n <p className=\"font-weight-bold mb-0 text-primary-500\">{lineName}</p>\n ) : (\n <div className=\"d-flex align-items-start\">\n <EdxUnlimitedLogo ariaLabel=\"edX Plus\" />\n <div>\n <p className=\"font-weight-bold mb-0 text-primary-500\">{title}</p>\n <p className=\"small mb-0 text-gray-900\">{blurb}</p>\n </div>\n </div>\n )}\n {ownedCourseNames && (\n <p\n className=\"cart-summary__already-yours small text-gray-700 mb-0 mt-2\"\n data-testid=\"already-yours\"\n >\n {formatMessage(messages.alreadyYours, { names: ownedCourseNames })}\n </p>\n )}\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"cart-summary__subtotal-row d-flex justify-content-between align-items-center p-0 pt-3\">\n <span className=\"cart-summary__subtotal text-gray-900\">\n {formatMessage(messages.subtotal)}\n </span>\n <span className=\"cart-summary__subtotal text-gray-900\">\n {subtotalText}\n </span>\n </Card.Section>\n\n {deduction && (\n <Card.Section\n className=\"d-flex justify-content-between align-items-center p-0\"\n data-testid=\"deduction-row\"\n >\n <span className=\"text-gray-700\">\n {formatMessage(messages.deduction)}\n </span>\n <span className=\"text-gray-700\">\n {formatMessage(messages.deductionAmount, {\n amount: deduction.amount.toFixed(2),\n currency: deduction.currency,\n })}\n </span>\n </Card.Section>\n )}\n\n {discount && (\n <Card.Section\n className=\"d-flex justify-content-between align-items-center p-0\"\n data-testid=\"discount-row\"\n >\n <span className=\"text-success font-weight-bold\">\n {formatMessage(messages.discountApplied)}\n </span>\n <span className=\"text-success font-weight-bold\">\n {formatMessage(messages.discountAmount, {\n amount: discount.amount.toFixed(2),\n currency: discount.currency,\n })}\n </span>\n </Card.Section>\n )}\n\n <Card.Section className=\"d-flex justify-content-between align-items-center p-0\">\n <span className=\"cart-summary__total-label text-gray-900\">\n {formatMessage(messages.todaysTotal)}\n </span>\n <span className=\"cart-summary__total-value mb-0 text-gray-900\">\n {totalText}\n </span>\n </Card.Section>\n </Card>\n\n {promoCode ? <PromoCode {...promoCode} /> : null}\n\n {/* Stats row */}\n <div className=\"cart-summary__stats d-none d-lg-flex\">\n <div className=\"cart-summary__stat d-flex align-items-center mr-4\">\n <img src={learnersIcon} alt=\"\" width={50} height={50} className=\"mr-2\" />\n <div>\n <p className=\"cart-summary__stat-count font-weight-bold text-gray-500 mb-0\">\n {formatMessage(messages.learnersCount)}\n </p>\n <p className=\"cart-summary__stat-label text-gray-500 mb-0\">\n {formatMessage(messages.learnersLabel)}\n </p>\n </div>\n </div>\n <div className=\"cart-summary__stat d-flex align-items-center\">\n <img src={institutionsIcon} alt=\"\" width={50} height={50} className=\"mr-2\" />\n <div>\n <p className=\"cart-summary__stat-count font-weight-bold text-gray-500 mb-0\">\n {formatMessage(messages.institutionsCount)}\n </p>\n <p className=\"cart-summary__stat-label text-gray-500 mb-0\">\n {formatMessage(messages.institutionsLabel)}\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default CartSummary;\n"]}
|
|
@@ -20,7 +20,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
20
20
|
};
|
|
21
21
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
22
|
import { useCallback, useEffect, useState } from 'react';
|
|
23
|
-
import { Alert, Col, Row } from '@openedx/paragon';
|
|
23
|
+
import { Alert, Button, Col, Row } from '@openedx/paragon';
|
|
24
24
|
import { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';
|
|
25
25
|
import { useIntl } from '@openedx/frontend-base';
|
|
26
26
|
import useCheckout from '../../data/hooks/useCheckout';
|
|
@@ -44,17 +44,18 @@ import messages from './Messages';
|
|
|
44
44
|
// Flip back to true to restore the redirect.
|
|
45
45
|
const REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;
|
|
46
46
|
const Checkout = (_a) => {
|
|
47
|
-
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
47
|
+
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
48
48
|
var { homeUrl = '/', orderHistoryUrl } = _a, props = __rest(_a, ["homeUrl", "orderHistoryUrl"]);
|
|
49
49
|
const { formatMessage } = useIntl();
|
|
50
50
|
const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });
|
|
51
51
|
const c = useCheckout(props);
|
|
52
52
|
const [promoCodeValue, setPromoCodeValue] = useState('');
|
|
53
53
|
const [dismissedCouponAlert, setDismissedCouponAlert] = useState(null);
|
|
54
|
+
const [showValidationBanner, setShowValidationBanner] = useState(false);
|
|
54
55
|
// Last code we successfully priced — drives the "Save {percent}% with code
|
|
55
56
|
// {code}" hint on the empty field while a coupon is not yet applied.
|
|
56
57
|
const [advertisedOffer, setAdvertisedOffer] = useState(null);
|
|
57
|
-
const isAlacarteReady = alacarte.phase === 'ready' && !!alacarte.checkout;
|
|
58
|
+
const isAlacarteReady = (alacarte.phase === 'ready' || alacarte.phase === 'alreadyOwned') && !!alacarte.checkout;
|
|
58
59
|
const appliedCouponCode = ((_c = (_b = alacarte.checkout) === null || _b === void 0 ? void 0 : _b.couponCode) === null || _c === void 0 ? void 0 : _c.trim())
|
|
59
60
|
? alacarte.checkout.couponCode.trim()
|
|
60
61
|
: null;
|
|
@@ -120,11 +121,14 @@ const Checkout = (_a) => {
|
|
|
120
121
|
const selectedCycle = c.selectedCycle;
|
|
121
122
|
// Prefer the à la carte course's own detail (skills/badge/image, richer than
|
|
122
123
|
// the subscription flow's ?course_run_key= lookup) when we selected a course.
|
|
123
|
-
|
|
124
|
+
// When already-owned the line may be null, so fall back to the selector kind.
|
|
125
|
+
const isCourseProduct = ((_f = alacarte.line) === null || _f === void 0 ? void 0 : _f.kind) === 'course' || ((_g = alacarte.selector) === null || _g === void 0 ? void 0 : _g.kind) === 'course';
|
|
126
|
+
const isProgramProduct = ((_h = alacarte.line) === null || _h === void 0 ? void 0 : _h.kind) === 'program' || ((_j = alacarte.selector) === null || _j === void 0 ? void 0 : _j.kind) === 'program';
|
|
127
|
+
const alacarteCourseDetail = isCourseProduct && alacarte.productDetail && 'maId' in alacarte.productDetail
|
|
124
128
|
? alacarte.productDetail
|
|
125
129
|
: null;
|
|
126
130
|
const courseForSummary = alacarteCourseDetail !== null && alacarteCourseDetail !== void 0 ? alacarteCourseDetail : c.course;
|
|
127
|
-
const alacarteProgramDetail =
|
|
131
|
+
const alacarteProgramDetail = isProgramProduct && alacarte.productDetail && 'tCourses' in alacarte.productDetail
|
|
128
132
|
? alacarte.productDetail
|
|
129
133
|
: null;
|
|
130
134
|
const handleStartCourse = () => {
|
|
@@ -159,9 +163,9 @@ const Checkout = (_a) => {
|
|
|
159
163
|
// tPlacedOrder is still polling, keep the checkout form so Pay now can show
|
|
160
164
|
// "Processing..." instead of a full-page loader.
|
|
161
165
|
if (alacarte.placedOrder || alacarte.placedOrderError) {
|
|
162
|
-
const confirmationTitle = ((
|
|
166
|
+
const confirmationTitle = ((_k = alacarte.line) === null || _k === void 0 ? void 0 : _k.kind) === 'program'
|
|
163
167
|
? alacarte.line.programName
|
|
164
|
-
: ((
|
|
168
|
+
: ((_l = alacarte.line) === null || _l === void 0 ? void 0 : _l.kind) === 'course'
|
|
165
169
|
? alacarte.line.courseName
|
|
166
170
|
: null;
|
|
167
171
|
return (_jsxs(_Fragment, { children: [alacarteDebug, _jsx(Row, { className: "checkout__container bg-light-200", children: _jsx(Col, { xs: 12, children: _jsx(AlacarteConfirmation, { order: alacarte.placedOrder, awaiting: false, error: alacarte.placedOrderError, onContinue: handleStartCourse, productDetail: alacarte.productDetail, productTitle: confirmationTitle, orderHistoryUrl: orderHistoryUrl !== null && orderHistoryUrl !== void 0 ? orderHistoryUrl : null }) }) })] }));
|
|
@@ -174,17 +178,23 @@ const Checkout = (_a) => {
|
|
|
174
178
|
// notice while that happens.
|
|
175
179
|
return (_jsxs(_Fragment, { children: [alacarteDebug, _jsx(ProcessingScreen, {})] }));
|
|
176
180
|
}
|
|
177
|
-
if (alacarte.phase === '
|
|
181
|
+
if (alacarte.phase === 'noActiveCheckout') {
|
|
178
182
|
return (_jsxs(_Fragment, { children: [alacarteDebug, _jsx(AlacarteNotice, { messageId: alacarte.noticeMessageId })] }));
|
|
179
183
|
}
|
|
180
|
-
|
|
184
|
+
if (alacarte.phase === 'notPurchasable') {
|
|
185
|
+
return (_jsxs(_Fragment, { children: [alacarteDebug, _jsx(Row, { className: "checkout__container bg-light-200", children: _jsx(Col, { xs: 12, children: _jsx(Alert, { variant: "danger", icon: ErrorIcon, children: alacarte.error }) }) })] }));
|
|
186
|
+
}
|
|
187
|
+
const isAlreadyOwned = alacarte.isAlreadyOwned;
|
|
188
|
+
return (_jsxs(_Fragment, { children: [alacarteDebug, _jsxs(Row, { className: "checkout__container bg-light-200", children: [_jsx(CountryUnavailableModal, { isOpen: isBillingCountryBlocked, homeUrl: homeUrl }), _jsx(Col, { xs: 12, className: "d-lg-none", children: _jsx("h1", { className: "font-weight-bold text-primary-900", children: "Checkout" }) }), _jsx(Col, { xs: 12, className: isAlreadyOwned ? 'checkout__owned-overlay' : '', children: _jsxs(Row, { className: "flex-column-reverse gap-5 flex-lg-row", children: [_jsx(Col, { xs: 12, lg: 7, xxl: 9, className: "payment__options", children: _jsxs("div", { children: [_jsx("h1", { className: "font-weight-bold text-primary-900 d-none d-lg-block mb-4", children: "Checkout" }), isAlreadyOwned && (_jsxs(Alert, { variant: "danger", icon: ErrorIcon, className: "checkout__already-owned-alert mb-3", "data-testid": "already-owned-alert", children: [_jsx("p", { className: "mb-3", children: formatMessage(((_m = alacarte.selector) === null || _m === void 0 ? void 0 : _m.kind) === 'program'
|
|
189
|
+
? messages.alreadyPaidProgram
|
|
190
|
+
: messages.alreadyPaidCourse) }), _jsxs("div", { className: "d-flex align-items-center justify-content-end gap-3 flex-wrap", children: [orderHistoryUrl && (_jsx("a", { href: orderHistoryUrl, className: "text-gray-900 font-weight-bold", children: formatMessage(messages.viewOrderHistory) })), _jsx(Button, { variant: "brand", className: "rounded-pill", onClick: handleStartCourse, children: formatMessage(messages.goToCourse) })] })] })), c.error && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, dismissible: true, onClose: c.clearError, children: c.error })), alacarte.placeOrderError && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, dismissible: true, onClose: alacarte.clearPlaceOrderError, className: "mb-3", children: alacarte.placeOrderError })), alacarte.couponError && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, dismissible: true, onClose: alacarte.clearCouponError, className: "mb-3", "data-testid": "coupon-error-alert", children: alacarte.couponError })), !alacarte.priceAccepted && isAlacarteReady && ((_o = alacarte.line) === null || _o === void 0 ? void 0 : _o.amountDue) && (_jsx(Alert, { variant: "info", className: "mb-3", "data-testid": "price-accept-notice", children: formatMessage(messages.priceChangedNotice, {
|
|
181
191
|
amount: alacarte.line.amountDue.amount.toFixed(2),
|
|
182
192
|
currency: alacarte.line.amountDue.currency,
|
|
183
|
-
}) })), showForm && (_jsxs(_Fragment, { children: [c.phase === 'failed' && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, className: "mb-3", children: failedMessage
|
|
193
|
+
}) })), showForm && (_jsxs(_Fragment, { children: [showValidationBanner && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, className: "mb-3", children: formatMessage(messages.validationBanner) })), c.phase === 'failed' && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, className: "mb-3", children: failedMessage
|
|
184
194
|
|| formatMessage(messages.paymentProcessingDescription) })), appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (_jsxs(Alert, { variant: "success", icon: CheckCircle, dismissible: true, closeLabel: formatMessage(messages.dismiss), onClose: () => setDismissedCouponAlert(appliedCouponCode), className: "coupon-applied-alert", "data-testid": "coupon-applied-alert", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83C\uDF89 " }), formatMessage(messages.couponAppliedAlert, {
|
|
185
195
|
code: appliedCouponCode,
|
|
186
196
|
percent: appliedDiscount.percent,
|
|
187
|
-
})] })), _jsx(PaymentForm, { billing: c.billing, setField: c.setField, numberMountRef: c.numberMountRef, expiryMountRef: c.expiryMountRef, cvcMountRef: c.cvcMountRef, cardError: c.cardError, stripeReady: c.stripeReady, sessionReady: c.sessionReady, submitting: isAlacarteReady
|
|
197
|
+
})] })), _jsx(PaymentForm, { billing: c.billing, setField: c.setField, numberMountRef: c.numberMountRef, expiryMountRef: c.expiryMountRef, cvcMountRef: c.cvcMountRef, cardError: c.cardError, cardFieldStates: c.cardFieldStates, stripeReady: c.stripeReady, sessionReady: c.sessionReady, submitting: isAlacarteReady
|
|
188
198
|
? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)
|
|
189
199
|
: c.processingSubscription, onPlaceOrder: isAlacarteReady
|
|
190
200
|
? () => {
|
|
@@ -196,7 +206,7 @@ const Checkout = (_a) => {
|
|
|
196
206
|
confirmCardPayment: c.confirmCardPayment,
|
|
197
207
|
});
|
|
198
208
|
}
|
|
199
|
-
: c.placeOrder, trialEnd: selectedCycle === 'monthly' ? displayEnd : undefined, priceToShow: null, cycleToShow: selectedCycle === 'monthly' ? 'monthly' : 'annual', userCountry: c.userCountry, geolocationUrl: props.geolocationUrl, alacarteLine: alacarte.line, stripe: c.stripe, expressCountry: c.billing.country || c.userCountry || 'US', onExpressPayment: isAlacarteReady ? handleExpressPayment : undefined })] })), c.phase === 'success' && (_jsx(SuccessScreen, { subscriptionBiId: ((
|
|
209
|
+
: c.placeOrder, onValidationError: setShowValidationBanner, trialEnd: selectedCycle === 'monthly' ? displayEnd : undefined, priceToShow: null, cycleToShow: selectedCycle === 'monthly' ? 'monthly' : 'annual', userCountry: c.userCountry, geolocationUrl: props.geolocationUrl, alacarteLine: alacarte.line, stripe: c.stripe, expressCountry: c.billing.country || c.userCountry || 'US', onExpressPayment: isAlacarteReady ? handleExpressPayment : undefined })] })), c.phase === 'success' && (_jsx(SuccessScreen, { subscriptionBiId: ((_p = c.subscriptionDetails) === null || _p === void 0 ? void 0 : _p.biId) || '', cardLast4: ((_q = c.subscriptionDetails) === null || _q === void 0 ? void 0 : _q.cardLast4) || '', purchaseDate: displayStart, courseLabel: courseLabel, onFindCourse: handleStartCourse })), c.phase === 'blocked' && (_jsx(BlockedScreen, { detail: c.lastComment }))] }) }), _jsx(Col, { xs: 12, lg: 5, xxl: 3, className: "payment__summary", children: c.phase === 'success' ? (_jsx(EnrollmentSummary, { course: c.course, onStartCourse: handleStartCourse })) : (_jsx(CartSummary, { defaultCycle: selectedCycle, onCycleChange: c.processingSubscription ? undefined : c.setCycle, course: courseForSummary, alacarteLine: alacarte.line, programDetail: alacarteProgramDetail, programName: alacarte.ownedProductName, promoCode: isAlacarteReady ? {
|
|
200
210
|
value: promoCodeValue,
|
|
201
211
|
onChange: (next) => {
|
|
202
212
|
setPromoCodeValue(next);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useState } from 'react';\nimport { Alert, Col, Row } from '@openedx/paragon';\nimport { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';\nimport { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport AlacarteNotice from '../AlacarteNotice/AlacarteNotice';\nimport AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';\nimport AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel\nimport { getTrialDateRange } from '@src/utils';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { appliedDiscountPercent } from '../../utils/discount';\nimport { confirmPaymentWithMethodId } from '../../utils/stripeCard';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle, CourseDetail } from '../../types';\nimport type { ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\n// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a\n// banner instead so the case is visible while the flow is being verified.\n// Flip back to true to restore the redirect.\nconst REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** Endpoint used to look up the representative IP for a selected country. */\n geolocationUrl?: string,\n /** Learner dashboard / order history URL for confirmation CTAs. */\n orderHistoryUrl?: string,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n orderHistoryUrl,\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });\n const c = useCheckout(props);\n const [promoCodeValue, setPromoCodeValue] = useState('');\n const [dismissedCouponAlert, setDismissedCouponAlert] = useState<string | null>(null);\n // Last code we successfully priced — drives the \"Save {percent}% with code\n // {code}\" hint on the empty field while a coupon is not yet applied.\n const [advertisedOffer, setAdvertisedOffer] = useState<{ code: string, percent: number } | null>(null);\n const isAlacarteReady = alacarte.phase === 'ready' && !!alacarte.checkout;\n const appliedCouponCode = alacarte.checkout?.couponCode?.trim()\n ? alacarte.checkout.couponCode.trim()\n : null;\n const discountPercent = appliedDiscountPercent(alacarte.line);\n const appliedDiscount = appliedCouponCode && alacarte.line?.discount && discountPercent != null\n ? {\n percent: discountPercent,\n amount: alacarte.line.discount.amount,\n currency: alacarte.line.discount.currency,\n }\n : null;\n\n // Keep the coupon field in sync when a code is applied via URL or API.\n useEffect(() => {\n if (alacarte.checkout?.couponCode) {\n setPromoCodeValue(alacarte.checkout.couponCode);\n }\n }, [alacarte.checkout?.couponCode]);\n\n useEffect(() => {\n if (appliedCouponCode && discountPercent != null) {\n setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });\n }\n }, [appliedCouponCode, discountPercent]);\n\n // No query params and no active cart — nothing to check out, send the\n // learner home rather than showing a placeholder.\n useEffect(() => {\n if (REDIRECT_ON_NO_ACTIVE_CHECKOUT && alacarte.phase === 'noActiveCheckout') {\n window.location.href = homeUrl;\n }\n }, [alacarte.phase, homeUrl]);\n\n // Express checkout (Apple Pay / Google Pay): runs the full place-order flow\n // using a wallet-provided PaymentMethod id instead of the mounted card element.\n const handleExpressPayment = useCallback(async (paymentMethodId: string, walletCountry?: string) => {\n if (!c.stripe) throw new Error('Stripe not loaded');\n const stripeInstance = c.stripe;\n\n await alacarte.placeOrderAsync({\n billing: {\n ...c.billing,\n country: c.billing.country || walletCountry || c.userCountry || '',\n },\n confirmCardPayment: async (clientSecret: string) => {\n await confirmPaymentWithMethodId({\n stripe: stripeInstance,\n clientSecret,\n paymentMethodId,\n });\n },\n });\n }, [alacarte, c.billing, c.userCountry, c.stripe]);\n\n const showProcessing\n = c.returningFromStripe\n && c.processingSubscription\n && c.phase !== 'success'\n && c.phase !== 'blocked'\n && c.phase !== 'failed';\n const showForm\n = !showProcessing\n && (c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing');\n const failedMessage = getLearnerFacingCheckoutMessage(c.lastComment);\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const isBillingCountryBlocked = GATE_CHECK_COUNTRY\n && !!c.billing.country\n && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);\n\n const selectedCycle: BillingCycle = c.selectedCycle;\n\n // Prefer the à la carte course's own detail (skills/badge/image, richer than\n // the subscription flow's ?course_run_key= lookup) when we selected a course.\n const alacarteCourseDetail: CourseDetail | null\n = alacarte.line?.kind === 'course' && alacarte.productDetail && 'maId' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n const courseForSummary = alacarteCourseDetail ?? c.course;\n\n const alacarteProgramDetail: ProgramDetail | null\n = alacarte.line?.kind === 'program' && alacarte.productDetail && 'tCourses' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n\n const handleStartCourse = () => {\n const alacarteCourseUrl = alacarteCourseDetail?.tUrlCourse\n || (alacarte.productDetail && 'tUrlCourse' in alacarte.productDetail\n ? alacarte.productDetail.tUrlCourse\n : null);\n if (alacarteCourseUrl) {\n window.location.href = alacarteCourseUrl;\n } else if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = homeUrl || '/';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n if (showProcessing) {\n return <ProcessingScreen />;\n }\n\n // TEMPORARY — flip to true to dump à la carte hook state while validating\n // against the real backend. Remove AlacarteDebugPanel once the flow is solid.\n const SHOW_ALACARTE_DEBUG = false;\n const alacarteDebug = SHOW_ALACARTE_DEBUG\n ? <AlacarteDebugPanel data={alacarte} />\n : null;\n\n // Confirmation: show once the order is ready (or failed to load). While\n // tPlacedOrder is still polling, keep the checkout form so Pay now can show\n // \"Processing...\" instead of a full-page loader.\n if (alacarte.placedOrder || alacarte.placedOrderError) {\n const confirmationTitle = alacarte.line?.kind === 'program'\n ? alacarte.line.programName\n : alacarte.line?.kind === 'course'\n ? alacarte.line.courseName\n : null;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <AlacarteConfirmation\n order={alacarte.placedOrder}\n awaiting={false}\n error={alacarte.placedOrderError}\n onContinue={handleStartCourse}\n productDetail={alacarte.productDetail}\n productTitle={confirmationTitle}\n orderHistoryUrl={orderHistoryUrl ?? null}\n />\n </Col>\n </Row>\n </>\n );\n }\n\n if (alacarte.phase === 'loading') {\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout' && REDIRECT_ON_NO_ACTIVE_CHECKOUT) {\n // Redirects home via the effect above — show a spinner rather than a\n // notice while that happens.\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'alreadyOwned' || alacarte.phase === 'noActiveCheckout') {\n return (\n <>\n {alacarteDebug}\n <AlacarteNotice messageId={alacarte.noticeMessageId!} />\n </>\n );\n }\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <CountryUnavailableModal\n isOpen={isBillingCountryBlocked}\n homeUrl={homeUrl}\n />\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n <Col xs={12}>\n <Row className=\"flex-column-reverse gap-5 flex-lg-row\">\n {/* ── LEFT ── */}\n <Col xs={12} lg={7} xxl={9} className=\"payment__options\">\n <div>\n <h1 className=\"font-weight-bold text-primary-900 d-none d-lg-block mb-4\">\n Checkout\n </h1>\n {c.error && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={c.clearError}\n >\n {c.error}\n </Alert>\n )}\n\n {alacarte.placeOrderError && (\n <Alert\n variant=\"warning\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearPlaceOrderError}\n className=\"mb-3\"\n >\n {alacarte.placeOrderError}\n </Alert>\n )}\n\n {!alacarte.priceAccepted && isAlacarteReady && alacarte.line?.amountDue && (\n <Alert variant=\"info\" className=\"mb-3\" data-testid=\"price-accept-notice\">\n {formatMessage(messages.priceChangedNotice, {\n amount: alacarte.line.amountDue.amount.toFixed(2),\n currency: alacarte.line.amountDue.currency,\n })}\n </Alert>\n )}\n\n {showForm && (\n <>\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || formatMessage(messages.paymentProcessingDescription)}\n </Alert>\n )}\n {appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={() => setDismissedCouponAlert(appliedCouponCode)}\n className=\"coupon-applied-alert\"\n data-testid=\"coupon-applied-alert\"\n >\n <span aria-hidden=\"true\">🎉 </span>\n {formatMessage(messages.couponAppliedAlert, {\n code: appliedCouponCode,\n percent: appliedDiscount.percent,\n })}\n </Alert>\n )}\n <PaymentForm\n billing={c.billing}\n setField={c.setField}\n numberMountRef={c.numberMountRef}\n expiryMountRef={c.expiryMountRef}\n cvcMountRef={c.cvcMountRef}\n cardError={c.cardError}\n stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={\n isAlacarteReady\n ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)\n : c.processingSubscription\n }\n onPlaceOrder={\n isAlacarteReady\n ? () => {\n alacarte.placeOrder({\n billing: {\n ...c.billing,\n // Country is required on tCheckoutPlaceOrder (two-letter ISO).\n // Prefer the form selection; fall back to gate geolocation.\n country: c.billing.country || c.userCountry || '',\n },\n confirmCardPayment: c.confirmCardPayment,\n });\n }\n : c.placeOrder\n }\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={null}\n cycleToShow={selectedCycle === 'monthly' ? 'monthly' : 'annual'}\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n alacarteLine={alacarte.line}\n stripe={c.stripe}\n expressCountry={c.billing.country || c.userCountry || 'US'}\n onExpressPayment={isAlacarteReady ? handleExpressPayment : undefined}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n courseLabel={courseLabel}\n onFindCourse={handleStartCourse}\n />\n )}\n\n {c.phase === 'blocked' && (\n <BlockedScreen detail={c.lastComment} />\n )}\n </div>\n </Col>\n\n {/* ── RIGHT — swaps to EnrollmentSummary on success ── */}\n <Col xs={12} lg={5} xxl={3} className=\"payment__summary\">\n {c.phase === 'success' ? (\n <EnrollmentSummary\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={courseForSummary}\n alacarteLine={alacarte.line}\n programDetail={alacarteProgramDetail}\n promoCode={isAlacarteReady ? {\n value: promoCodeValue,\n onChange: (next) => {\n setPromoCodeValue(next);\n if (alacarte.couponError) alacarte.clearCouponError();\n },\n onApply: () => alacarte.applyCoupon(promoCodeValue),\n onRemove: () => {\n alacarte.removeCoupon();\n setPromoCodeValue('');\n setAdvertisedOffer(null);\n },\n appliedCode: appliedCouponCode,\n discount: appliedDiscount,\n offer: !appliedCouponCode ? advertisedOffer : null,\n error: alacarte.couponError,\n isApplying: alacarte.couponApplying,\n disabled: alacarte.placingOrder,\n } : null}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n </>\n );\n};\n\nexport default Checkout;\n"]}
|
|
1
|
+
{"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useState } from 'react';\nimport { Alert, Button, Col, Row } from '@openedx/paragon';\nimport { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';\nimport { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport AlacarteNotice from '../AlacarteNotice/AlacarteNotice';\nimport AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';\nimport AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel\nimport { getTrialDateRange } from '@src/utils';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { appliedDiscountPercent } from '../../utils/discount';\nimport { confirmPaymentWithMethodId } from '../../utils/stripeCard';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle, CourseDetail } from '../../types';\nimport type { ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\n// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a\n// banner instead so the case is visible while the flow is being verified.\n// Flip back to true to restore the redirect.\nconst REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** Endpoint used to look up the representative IP for a selected country. */\n geolocationUrl?: string,\n /** Learner dashboard / order history URL for confirmation CTAs. */\n orderHistoryUrl?: string,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n orderHistoryUrl,\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });\n const c = useCheckout(props);\n const [promoCodeValue, setPromoCodeValue] = useState('');\n const [dismissedCouponAlert, setDismissedCouponAlert] = useState<string | null>(null);\n const [showValidationBanner, setShowValidationBanner] = useState(false);\n // Last code we successfully priced — drives the \"Save {percent}% with code\n // {code}\" hint on the empty field while a coupon is not yet applied.\n const [advertisedOffer, setAdvertisedOffer] = useState<{ code: string, percent: number } | null>(null);\n const isAlacarteReady = (alacarte.phase === 'ready' || alacarte.phase === 'alreadyOwned') && !!alacarte.checkout;\n const appliedCouponCode = alacarte.checkout?.couponCode?.trim()\n ? alacarte.checkout.couponCode.trim()\n : null;\n const discountPercent = appliedDiscountPercent(alacarte.line);\n const appliedDiscount = appliedCouponCode && alacarte.line?.discount && discountPercent != null\n ? {\n percent: discountPercent,\n amount: alacarte.line.discount.amount,\n currency: alacarte.line.discount.currency,\n }\n : null;\n\n // Keep the coupon field in sync when a code is applied via URL or API.\n useEffect(() => {\n if (alacarte.checkout?.couponCode) {\n setPromoCodeValue(alacarte.checkout.couponCode);\n }\n }, [alacarte.checkout?.couponCode]);\n\n useEffect(() => {\n if (appliedCouponCode && discountPercent != null) {\n setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });\n }\n }, [appliedCouponCode, discountPercent]);\n\n // No query params and no active cart — nothing to check out, send the\n // learner home rather than showing a placeholder.\n useEffect(() => {\n if (REDIRECT_ON_NO_ACTIVE_CHECKOUT && alacarte.phase === 'noActiveCheckout') {\n window.location.href = homeUrl;\n }\n }, [alacarte.phase, homeUrl]);\n\n // Express checkout (Apple Pay / Google Pay): runs the full place-order flow\n // using a wallet-provided PaymentMethod id instead of the mounted card element.\n const handleExpressPayment = useCallback(async (paymentMethodId: string, walletCountry?: string) => {\n if (!c.stripe) throw new Error('Stripe not loaded');\n const stripeInstance = c.stripe;\n\n await alacarte.placeOrderAsync({\n billing: {\n ...c.billing,\n country: c.billing.country || walletCountry || c.userCountry || '',\n },\n confirmCardPayment: async (clientSecret: string) => {\n await confirmPaymentWithMethodId({\n stripe: stripeInstance,\n clientSecret,\n paymentMethodId,\n });\n },\n });\n }, [alacarte, c.billing, c.userCountry, c.stripe]);\n\n const showProcessing\n = c.returningFromStripe\n && c.processingSubscription\n && c.phase !== 'success'\n && c.phase !== 'blocked'\n && c.phase !== 'failed';\n const showForm\n = !showProcessing\n && (c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing');\n const failedMessage = getLearnerFacingCheckoutMessage(c.lastComment);\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const isBillingCountryBlocked = GATE_CHECK_COUNTRY\n && !!c.billing.country\n && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);\n\n const selectedCycle: BillingCycle = c.selectedCycle;\n\n // Prefer the à la carte course's own detail (skills/badge/image, richer than\n // the subscription flow's ?course_run_key= lookup) when we selected a course.\n // When already-owned the line may be null, so fall back to the selector kind.\n const isCourseProduct = alacarte.line?.kind === 'course' || alacarte.selector?.kind === 'course';\n const isProgramProduct = alacarte.line?.kind === 'program' || alacarte.selector?.kind === 'program';\n\n const alacarteCourseDetail: CourseDetail | null\n = isCourseProduct && alacarte.productDetail && 'maId' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n const courseForSummary = alacarteCourseDetail ?? c.course;\n\n const alacarteProgramDetail: ProgramDetail | null\n = isProgramProduct && alacarte.productDetail && 'tCourses' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n\n const handleStartCourse = () => {\n const alacarteCourseUrl = alacarteCourseDetail?.tUrlCourse\n || (alacarte.productDetail && 'tUrlCourse' in alacarte.productDetail\n ? alacarte.productDetail.tUrlCourse\n : null);\n if (alacarteCourseUrl) {\n window.location.href = alacarteCourseUrl;\n } else if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = homeUrl || '/';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n if (showProcessing) {\n return <ProcessingScreen />;\n }\n\n // TEMPORARY — flip to true to dump à la carte hook state while validating\n // against the real backend. Remove AlacarteDebugPanel once the flow is solid.\n const SHOW_ALACARTE_DEBUG = false;\n const alacarteDebug = SHOW_ALACARTE_DEBUG\n ? <AlacarteDebugPanel data={alacarte} />\n : null;\n\n // Confirmation: show once the order is ready (or failed to load). While\n // tPlacedOrder is still polling, keep the checkout form so Pay now can show\n // \"Processing...\" instead of a full-page loader.\n if (alacarte.placedOrder || alacarte.placedOrderError) {\n const confirmationTitle = alacarte.line?.kind === 'program'\n ? alacarte.line.programName\n : alacarte.line?.kind === 'course'\n ? alacarte.line.courseName\n : null;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <AlacarteConfirmation\n order={alacarte.placedOrder}\n awaiting={false}\n error={alacarte.placedOrderError}\n onContinue={handleStartCourse}\n productDetail={alacarte.productDetail}\n productTitle={confirmationTitle}\n orderHistoryUrl={orderHistoryUrl ?? null}\n />\n </Col>\n </Row>\n </>\n );\n }\n\n if (alacarte.phase === 'loading') {\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout' && REDIRECT_ON_NO_ACTIVE_CHECKOUT) {\n // Redirects home via the effect above — show a spinner rather than a\n // notice while that happens.\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout') {\n return (\n <>\n {alacarteDebug}\n <AlacarteNotice messageId={alacarte.noticeMessageId!} />\n </>\n );\n }\n if (alacarte.phase === 'notPurchasable') {\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <Alert variant=\"danger\" icon={ErrorIcon}>\n {alacarte.error}\n </Alert>\n </Col>\n </Row>\n </>\n );\n }\n\n const isAlreadyOwned = alacarte.isAlreadyOwned;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <CountryUnavailableModal\n isOpen={isBillingCountryBlocked}\n homeUrl={homeUrl}\n />\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n\n <Col xs={12} className={isAlreadyOwned ? 'checkout__owned-overlay' : ''}>\n <Row className=\"flex-column-reverse gap-5 flex-lg-row\">\n {/* ── LEFT ── */}\n <Col xs={12} lg={7} xxl={9} className=\"payment__options\">\n <div>\n <h1 className=\"font-weight-bold text-primary-900 d-none d-lg-block mb-4\">\n Checkout\n </h1>\n\n {isAlreadyOwned && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"checkout__already-owned-alert mb-3\"\n data-testid=\"already-owned-alert\"\n >\n <p className=\"mb-3\">\n {formatMessage(\n alacarte.selector?.kind === 'program'\n ? messages.alreadyPaidProgram\n : messages.alreadyPaidCourse,\n )}\n </p>\n <div className=\"d-flex align-items-center justify-content-end gap-3 flex-wrap\">\n {orderHistoryUrl && (\n <a href={orderHistoryUrl} className=\"text-gray-900 font-weight-bold\">\n {formatMessage(messages.viewOrderHistory)}\n </a>\n )}\n <Button variant=\"brand\" className=\"rounded-pill\" onClick={handleStartCourse}>\n {formatMessage(messages.goToCourse)}\n </Button>\n </div>\n </Alert>\n )}\n\n {c.error && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={c.clearError}\n >\n {c.error}\n </Alert>\n )}\n\n {alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearPlaceOrderError}\n className=\"mb-3\"\n >\n {alacarte.placeOrderError}\n </Alert>\n )}\n\n {alacarte.couponError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearCouponError}\n className=\"mb-3\"\n data-testid=\"coupon-error-alert\"\n >\n {alacarte.couponError}\n </Alert>\n )}\n\n {!alacarte.priceAccepted && isAlacarteReady && alacarte.line?.amountDue && (\n <Alert variant=\"info\" className=\"mb-3\" data-testid=\"price-accept-notice\">\n {formatMessage(messages.priceChangedNotice, {\n amount: alacarte.line.amountDue.amount.toFixed(2),\n currency: alacarte.line.amountDue.currency,\n })}\n </Alert>\n )}\n\n {showForm && (\n <>\n {showValidationBanner && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {formatMessage(messages.validationBanner)}\n </Alert>\n )}\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || formatMessage(messages.paymentProcessingDescription)}\n </Alert>\n )}\n {appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={() => setDismissedCouponAlert(appliedCouponCode)}\n className=\"coupon-applied-alert\"\n data-testid=\"coupon-applied-alert\"\n >\n <span aria-hidden=\"true\">🎉 </span>\n {formatMessage(messages.couponAppliedAlert, {\n code: appliedCouponCode,\n percent: appliedDiscount.percent,\n })}\n </Alert>\n )}\n <PaymentForm\n billing={c.billing}\n setField={c.setField}\n numberMountRef={c.numberMountRef}\n expiryMountRef={c.expiryMountRef}\n cvcMountRef={c.cvcMountRef}\n cardError={c.cardError}\n cardFieldStates={c.cardFieldStates}\n stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={\n isAlacarteReady\n ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)\n : c.processingSubscription\n }\n onPlaceOrder={\n isAlacarteReady\n ? () => {\n alacarte.placeOrder({\n billing: {\n ...c.billing,\n // Country is required on tCheckoutPlaceOrder (two-letter ISO).\n // Prefer the form selection; fall back to gate geolocation.\n country: c.billing.country || c.userCountry || '',\n },\n confirmCardPayment: c.confirmCardPayment,\n });\n }\n : c.placeOrder\n }\n onValidationError={setShowValidationBanner}\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={null}\n cycleToShow={selectedCycle === 'monthly' ? 'monthly' : 'annual'}\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n alacarteLine={alacarte.line}\n stripe={c.stripe}\n expressCountry={c.billing.country || c.userCountry || 'US'}\n onExpressPayment={isAlacarteReady ? handleExpressPayment : undefined}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n courseLabel={courseLabel}\n onFindCourse={handleStartCourse}\n />\n )}\n\n {c.phase === 'blocked' && (\n <BlockedScreen detail={c.lastComment} />\n )}\n </div>\n </Col>\n\n {/* ── RIGHT — swaps to EnrollmentSummary on success ── */}\n <Col xs={12} lg={5} xxl={3} className=\"payment__summary\">\n {c.phase === 'success' ? (\n <EnrollmentSummary\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={courseForSummary}\n alacarteLine={alacarte.line}\n programDetail={alacarteProgramDetail}\n programName={alacarte.ownedProductName}\n promoCode={isAlacarteReady ? {\n value: promoCodeValue,\n onChange: (next) => {\n setPromoCodeValue(next);\n if (alacarte.couponError) alacarte.clearCouponError();\n },\n onApply: () => alacarte.applyCoupon(promoCodeValue),\n onRemove: () => {\n alacarte.removeCoupon();\n setPromoCodeValue('');\n setAdvertisedOffer(null);\n },\n appliedCode: appliedCouponCode,\n discount: appliedDiscount,\n offer: !appliedCouponCode ? advertisedOffer : null,\n error: alacarte.couponError,\n isApplying: alacarte.couponApplying,\n disabled: alacarte.placingOrder,\n } : null}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n </>\n );\n};\n\nexport default Checkout;\n"]}
|
|
@@ -19,5 +19,30 @@ declare const messages: {
|
|
|
19
19
|
defaultMessage: string;
|
|
20
20
|
description: string;
|
|
21
21
|
};
|
|
22
|
+
validationBanner: {
|
|
23
|
+
id: string;
|
|
24
|
+
defaultMessage: string;
|
|
25
|
+
description: string;
|
|
26
|
+
};
|
|
27
|
+
alreadyPaidCourse: {
|
|
28
|
+
id: string;
|
|
29
|
+
defaultMessage: string;
|
|
30
|
+
description: string;
|
|
31
|
+
};
|
|
32
|
+
alreadyPaidProgram: {
|
|
33
|
+
id: string;
|
|
34
|
+
defaultMessage: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
viewOrderHistory: {
|
|
38
|
+
id: string;
|
|
39
|
+
defaultMessage: string;
|
|
40
|
+
description: string;
|
|
41
|
+
};
|
|
42
|
+
goToCourse: {
|
|
43
|
+
id: string;
|
|
44
|
+
defaultMessage: string;
|
|
45
|
+
description: string;
|
|
46
|
+
};
|
|
22
47
|
};
|
|
23
48
|
export default messages;
|
|
@@ -20,6 +20,31 @@ const messages = defineMessages({
|
|
|
20
20
|
defaultMessage: 'Dismiss',
|
|
21
21
|
description: 'Closes the coupon-applied success alert',
|
|
22
22
|
},
|
|
23
|
+
validationBanner: {
|
|
24
|
+
id: 'checkout.validationBanner',
|
|
25
|
+
defaultMessage: 'Please complete the required fields highlighted below.',
|
|
26
|
+
description: 'Error banner shown when required payment fields are not filled in',
|
|
27
|
+
},
|
|
28
|
+
alreadyPaidCourse: {
|
|
29
|
+
id: 'checkout.alreadyPaid.course',
|
|
30
|
+
defaultMessage: "You've already paid for this course. You haven't been charged again.",
|
|
31
|
+
description: 'Alert shown when the learner already owns the course',
|
|
32
|
+
},
|
|
33
|
+
alreadyPaidProgram: {
|
|
34
|
+
id: 'checkout.alreadyPaid.program',
|
|
35
|
+
defaultMessage: "You've already paid for this program. You haven't been charged again.",
|
|
36
|
+
description: 'Alert shown when the learner already owns the program',
|
|
37
|
+
},
|
|
38
|
+
viewOrderHistory: {
|
|
39
|
+
id: 'checkout.alreadyPaid.viewOrderHistory',
|
|
40
|
+
defaultMessage: 'View order history',
|
|
41
|
+
description: 'Link to order history on the already-paid alert',
|
|
42
|
+
},
|
|
43
|
+
goToCourse: {
|
|
44
|
+
id: 'checkout.alreadyPaid.goToCourse',
|
|
45
|
+
defaultMessage: 'Go to course',
|
|
46
|
+
description: 'Button to navigate to the course on the already-paid alert',
|
|
47
|
+
},
|
|
23
48
|
});
|
|
24
49
|
export default messages;
|
|
25
50
|
//# sourceMappingURL=Messages.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/Checkout/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,4BAA4B,EAAE;QAC5B,EAAE,EAAE,mDAAmD;QACvD,cAAc,EAAE,oFAAoF;QACpG,WAAW,EAAE,gCAAgC;KAC9C;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,0FAA0F;QAC1G,WAAW,EAAE,2FAA2F;KACzG;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,6BAA6B;QACjC,cAAc,EAAE,gDAAgD;QAChE,WAAW,EAAE,qEAAqE;KACnF;IACD,OAAO,EAAE;QACP,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,yCAAyC;KACvD;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n paymentProcessingDescription: {\n id: 'checkout.paymentForm.paymentProcessingdescription',\n defaultMessage: 'Your payment could not be processed. Please check your card details and try again.',\n description: 'Payment processing description',\n },\n priceChangedNotice: {\n id: 'checkout.alacarte.priceChangedNotice',\n defaultMessage: 'The price is now ${amount} {currency}. Click Pay now to accept this amount and continue.',\n description: 'Shown when checkout.priceAccepted is false so the learner confirms the current amount due',\n },\n couponAppliedAlert: {\n id: 'checkout.couponAppliedAlert',\n defaultMessage: 'Coupon code {code} applied for {percent}% off!',\n description: 'Success alert shown above payment methods after a coupon is applied',\n },\n dismiss: {\n id: 'checkout.couponAppliedAlert.dismiss',\n defaultMessage: 'Dismiss',\n description: 'Closes the coupon-applied success alert',\n },\n});\n\nexport default messages;\n"]}
|
|
1
|
+
{"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/Checkout/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,4BAA4B,EAAE;QAC5B,EAAE,EAAE,mDAAmD;QACvD,cAAc,EAAE,oFAAoF;QACpG,WAAW,EAAE,gCAAgC;KAC9C;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,0FAA0F;QAC1G,WAAW,EAAE,2FAA2F;KACzG;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,6BAA6B;QACjC,cAAc,EAAE,gDAAgD;QAChE,WAAW,EAAE,qEAAqE;KACnF;IACD,OAAO,EAAE;QACP,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,yCAAyC;KACvD;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,2BAA2B;QAC/B,cAAc,EAAE,wDAAwD;QACxE,WAAW,EAAE,mEAAmE;KACjF;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,6BAA6B;QACjC,cAAc,EAAE,sEAAsE;QACtF,WAAW,EAAE,sDAAsD;KACpE;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,8BAA8B;QAClC,cAAc,EAAE,uEAAuE;QACvF,WAAW,EAAE,uDAAuD;KACrE;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,oBAAoB;QACpC,WAAW,EAAE,iDAAiD;KAC/D;IACD,UAAU,EAAE;QACV,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,4DAA4D;KAC1E;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n paymentProcessingDescription: {\n id: 'checkout.paymentForm.paymentProcessingdescription',\n defaultMessage: 'Your payment could not be processed. Please check your card details and try again.',\n description: 'Payment processing description',\n },\n priceChangedNotice: {\n id: 'checkout.alacarte.priceChangedNotice',\n defaultMessage: 'The price is now ${amount} {currency}. Click Pay now to accept this amount and continue.',\n description: 'Shown when checkout.priceAccepted is false so the learner confirms the current amount due',\n },\n couponAppliedAlert: {\n id: 'checkout.couponAppliedAlert',\n defaultMessage: 'Coupon code {code} applied for {percent}% off!',\n description: 'Success alert shown above payment methods after a coupon is applied',\n },\n dismiss: {\n id: 'checkout.couponAppliedAlert.dismiss',\n defaultMessage: 'Dismiss',\n description: 'Closes the coupon-applied success alert',\n },\n validationBanner: {\n id: 'checkout.validationBanner',\n defaultMessage: 'Please complete the required fields highlighted below.',\n description: 'Error banner shown when required payment fields are not filled in',\n },\n alreadyPaidCourse: {\n id: 'checkout.alreadyPaid.course',\n defaultMessage: \"You've already paid for this course. You haven't been charged again.\",\n description: 'Alert shown when the learner already owns the course',\n },\n alreadyPaidProgram: {\n id: 'checkout.alreadyPaid.program',\n defaultMessage: \"You've already paid for this program. You haven't been charged again.\",\n description: 'Alert shown when the learner already owns the program',\n },\n viewOrderHistory: {\n id: 'checkout.alreadyPaid.viewOrderHistory',\n defaultMessage: 'View order history',\n description: 'Link to order history on the already-paid alert',\n },\n goToCourse: {\n id: 'checkout.alreadyPaid.goToCourse',\n defaultMessage: 'Go to course',\n description: 'Button to navigate to the course on the already-paid alert',\n },\n});\n\nexport default messages;\n"]}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { Ref } from 'react';
|
|
2
|
+
export interface PaymentFieldErrors {
|
|
3
|
+
number?: string;
|
|
4
|
+
expiry?: string;
|
|
5
|
+
cvc?: string;
|
|
6
|
+
}
|
|
2
7
|
export interface PaymentFieldsProps {
|
|
3
8
|
numberMountRef: Ref<HTMLDivElement>;
|
|
4
9
|
expiryMountRef: Ref<HTMLDivElement>;
|
|
5
10
|
cvcMountRef: Ref<HTMLDivElement>;
|
|
11
|
+
fieldErrors?: PaymentFieldErrors;
|
|
6
12
|
}
|
|
7
|
-
declare const PaymentFields: ({ numberMountRef, expiryMountRef, cvcMountRef, }: PaymentFieldsProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
declare const PaymentFields: ({ numberMountRef, expiryMountRef, cvcMountRef, fieldErrors, }: PaymentFieldsProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
14
|
export default PaymentFields;
|
|
@@ -3,6 +3,11 @@ import { Form } from '@openedx/paragon';
|
|
|
3
3
|
import { STRIPE_FIELD_STYLE } from '../../constants';
|
|
4
4
|
import { CardBrands } from '../BrandMarks/BrandMarks';
|
|
5
5
|
import creditCardCvc from './creditCardCvc.png';
|
|
6
|
-
const
|
|
6
|
+
const FieldError = ({ message }) => {
|
|
7
|
+
if (!message)
|
|
8
|
+
return null;
|
|
9
|
+
return (_jsxs("div", { className: "payment--field__error", role: "alert", children: [_jsx("span", { className: "payment--field__error-icon", "aria-hidden": "true" }), message] }));
|
|
10
|
+
};
|
|
11
|
+
const PaymentFields = ({ numberMountRef, expiryMountRef, cvcMountRef, fieldErrors, }) => (_jsx("div", { className: "payment--field", children: _jsxs("div", { className: "d-flex flex-column mb-3 gap-4", children: [_jsxs(Form.Group, { controlId: "cardNumber", className: "mb-0 flex-grow-1", children: [_jsx(Form.Label, { className: "payment--field__label text-gray-900", children: "Card number (16 digits)" }), _jsxs("div", { className: `form-control${(fieldErrors === null || fieldErrors === void 0 ? void 0 : fieldErrors.number) ? ' payment--field__input--error' : ''}`, style: STRIPE_FIELD_STYLE, children: [_jsx("div", { ref: numberMountRef, className: "flex-grow-1" }), _jsx(CardBrands, {})] }), _jsx(FieldError, { message: fieldErrors === null || fieldErrors === void 0 ? void 0 : fieldErrors.number })] }), _jsxs("div", { className: "d-flex gap-4", children: [_jsxs(Form.Group, { controlId: "cardExpiry", className: "card__expiry mb-0 w-50", children: [_jsx(Form.Label, { className: "payment--field__label text-gray-900", children: "Expiration (MM/YY)" }), _jsx("div", { className: `form-control${(fieldErrors === null || fieldErrors === void 0 ? void 0 : fieldErrors.expiry) ? ' payment--field__input--error' : ''}`, style: STRIPE_FIELD_STYLE, children: _jsx("div", { ref: expiryMountRef, className: "flex-grow-1" }) }), _jsx(FieldError, { message: fieldErrors === null || fieldErrors === void 0 ? void 0 : fieldErrors.expiry })] }), _jsxs(Form.Group, { controlId: "cardCvc", className: "card__cvc mb-0 w-50", children: [_jsx(Form.Label, { className: "payment--field__label text-gray-900", children: "CVC (3-4 digits)" }), _jsxs("div", { className: `form-control${(fieldErrors === null || fieldErrors === void 0 ? void 0 : fieldErrors.cvc) ? ' payment--field__input--error' : ''}`, style: STRIPE_FIELD_STYLE, children: [_jsx("div", { ref: cvcMountRef, className: "flex-grow-1" }), _jsx("img", { src: creditCardCvc, alt: "", width: 24, height: 24 })] }), _jsx(FieldError, { message: fieldErrors === null || fieldErrors === void 0 ? void 0 : fieldErrors.cvc })] })] })] }) }));
|
|
7
12
|
export default PaymentFields;
|
|
8
13
|
//# sourceMappingURL=PaymentFields.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PaymentFields.js","sourceRoot":"","sources":["../../../src/components/PaymentFields/PaymentFields.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,aAAa,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"PaymentFields.js","sourceRoot":"","sources":["../../../src/components/PaymentFields/PaymentFields.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,aAAa,MAAM,qBAAqB,CAAC;AAehD,MAAM,UAAU,GAAG,CAAC,EAAE,OAAO,EAAwB,EAAE,EAAE;IACvD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,CACL,eAAK,SAAS,EAAC,uBAAuB,EAAC,IAAI,EAAC,OAAO,aACjD,eAAM,SAAS,EAAC,4BAA4B,iBAAa,MAAM,GAAG,EACjE,OAAO,IACJ,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,EACrB,cAAc,EACd,cAAc,EACd,WAAW,EACX,WAAW,GACQ,EAAE,EAAE,CAAC,CACxB,cAAK,SAAS,EAAC,gBAAgB,YAE7B,eAAK,SAAS,EAAC,+BAA+B,aAE5C,MAAC,IAAI,CAAC,KAAK,IAAC,SAAS,EAAC,YAAY,EAAC,SAAS,EAAC,kBAAkB,aAC7D,KAAC,IAAI,CAAC,KAAK,IAAC,SAAS,EAAC,qCAAqC,wCAE9C,EACb,eACE,SAAS,EAAE,eAAe,CAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,MAAM,EAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,EAAE,EAAE,EACtF,KAAK,EAAE,kBAAkB,aAEzB,cAAK,GAAG,EAAE,cAAc,EAAE,SAAS,EAAC,aAAa,GAAG,EACpD,KAAC,UAAU,KAAG,IACV,EACN,KAAC,UAAU,IAAC,OAAO,EAAE,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,MAAM,GAAI,IACjC,EAEb,eAAK,SAAS,EAAC,cAAc,aAE3B,MAAC,IAAI,CAAC,KAAK,IAAC,SAAS,EAAC,YAAY,EAAC,SAAS,EAAC,wBAAwB,aACnE,KAAC,IAAI,CAAC,KAAK,IAAC,SAAS,EAAC,qCAAqC,mCAE9C,EACb,cACE,SAAS,EAAE,eAAe,CAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,MAAM,EAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,EAAE,EAAE,EACtF,KAAK,EAAE,kBAAkB,YAEzB,cAAK,GAAG,EAAE,cAAc,EAAE,SAAS,EAAC,aAAa,GAAG,GAChD,EACN,KAAC,UAAU,IAAC,OAAO,EAAE,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,MAAM,GAAI,IACjC,EAGb,MAAC,IAAI,CAAC,KAAK,IAAC,SAAS,EAAC,SAAS,EAAC,SAAS,EAAC,qBAAqB,aAC7D,KAAC,IAAI,CAAC,KAAK,IAAC,SAAS,EAAC,qCAAqC,iCAA8B,EACzF,eACE,SAAS,EAAE,eAAe,CAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,GAAG,EAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,EAAE,EAAE,EACnF,KAAK,EAAE,kBAAkB,aAEzB,cAAK,GAAG,EAAE,WAAW,EAAE,SAAS,EAAC,aAAa,GAAG,EACjD,cAAK,GAAG,EAAE,aAAa,EAAE,GAAG,EAAC,EAAE,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,GAAI,IACrD,EACN,KAAC,UAAU,IAAC,OAAO,EAAE,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,GAAG,GAAI,IAC9B,IACT,IACF,GACF,CACP,CAAC;AAEF,eAAe,aAAa,CAAC","sourcesContent":["import React from 'react';\nimport type { Ref } from 'react';\nimport { Form } from '@openedx/paragon';\nimport { STRIPE_FIELD_STYLE } from '../../constants';\nimport { CardBrands } from '../BrandMarks/BrandMarks';\nimport creditCardCvc from './creditCardCvc.png';\n\nexport interface PaymentFieldErrors {\n number?: string,\n expiry?: string,\n cvc?: string,\n}\n\nexport interface PaymentFieldsProps {\n numberMountRef: Ref<HTMLDivElement>,\n expiryMountRef: Ref<HTMLDivElement>,\n cvcMountRef: Ref<HTMLDivElement>,\n fieldErrors?: PaymentFieldErrors,\n}\n\nconst FieldError = ({ message }: { message?: string }) => {\n if (!message) return null;\n return (\n <div className=\"payment--field__error\" role=\"alert\">\n <span className=\"payment--field__error-icon\" aria-hidden=\"true\" />\n {message}\n </div>\n );\n};\n\nconst PaymentFields = ({\n numberMountRef,\n expiryMountRef,\n cvcMountRef,\n fieldErrors,\n}: PaymentFieldsProps) => (\n <div className=\"payment--field\">\n {/* Single-row wrapper on desktop, stacked on mobile */}\n <div className=\"d-flex flex-column mb-3 gap-4\">\n {/* Card number — grows to fill available space */}\n <Form.Group controlId=\"cardNumber\" className=\"mb-0 flex-grow-1\">\n <Form.Label className=\"payment--field__label text-gray-900\">\n Card number (16 digits)\n </Form.Label>\n <div\n className={`form-control${fieldErrors?.number ? ' payment--field__input--error' : ''}`}\n style={STRIPE_FIELD_STYLE}\n >\n <div ref={numberMountRef} className=\"flex-grow-1\" />\n <CardBrands />\n </div>\n <FieldError message={fieldErrors?.number} />\n </Form.Group>\n\n <div className=\"d-flex gap-4\">\n {/* Expiry — fixed width on desktop */}\n <Form.Group controlId=\"cardExpiry\" className=\"card__expiry mb-0 w-50\">\n <Form.Label className=\"payment--field__label text-gray-900\">\n Expiration (MM/YY)\n </Form.Label>\n <div\n className={`form-control${fieldErrors?.expiry ? ' payment--field__input--error' : ''}`}\n style={STRIPE_FIELD_STYLE}\n >\n <div ref={expiryMountRef} className=\"flex-grow-1\" />\n </div>\n <FieldError message={fieldErrors?.expiry} />\n </Form.Group>\n\n {/* CVC — fixed width on desktop */}\n <Form.Group controlId=\"cardCvc\" className=\"card__cvc mb-0 w-50\">\n <Form.Label className=\"payment--field__label text-gray-900\">CVC (3-4 digits)</Form.Label>\n <div\n className={`form-control${fieldErrors?.cvc ? ' payment--field__input--error' : ''}`}\n style={STRIPE_FIELD_STYLE}\n >\n <div ref={cvcMountRef} className=\"flex-grow-1\" />\n <img src={creditCardCvc} alt=\"\" width={24} height={24} />\n </div>\n <FieldError message={fieldErrors?.cvc} />\n </Form.Group>\n </div>\n </div>\n </div>\n);\n\nexport default PaymentFields;\n"]}
|
|
@@ -63,6 +63,11 @@ const messages = defineMessages({
|
|
|
63
63
|
defaultMessage: 'Select your country',
|
|
64
64
|
description: 'Placeholder option shown before a country is selected',
|
|
65
65
|
},
|
|
66
|
+
fieldRequired: {
|
|
67
|
+
id: 'checkout.paymentForm.fieldRequired',
|
|
68
|
+
defaultMessage: 'This field is required',
|
|
69
|
+
description: 'Error text shown below a required field that is empty',
|
|
70
|
+
},
|
|
66
71
|
});
|
|
67
72
|
export default messages;
|
|
68
73
|
//# sourceMappingURL=Messages.js.map
|