@edx/frontend-app-alacarte-checkout 1.5.0 → 1.7.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.
Files changed (49) hide show
  1. package/dist/components/CartSummary/CartSummary.js +2 -2
  2. package/dist/components/CartSummary/CartSummary.js.map +1 -1
  3. package/dist/components/CartSummary/Messages.d.ts +4 -4
  4. package/dist/components/CartSummary/Messages.js +10 -10
  5. package/dist/components/CartSummary/Messages.js.map +1 -1
  6. package/dist/components/Checkout/Checkout.js +66 -12
  7. package/dist/components/Checkout/Checkout.js.map +1 -1
  8. package/dist/components/Checkout/Messages.d.ts +10 -0
  9. package/dist/components/Checkout/Messages.js +10 -0
  10. package/dist/components/Checkout/Messages.js.map +1 -1
  11. package/dist/components/ExpressCheckout/ExpressCheckout.d.ts +20 -1
  12. package/dist/components/ExpressCheckout/ExpressCheckout.js +77 -2
  13. package/dist/components/ExpressCheckout/ExpressCheckout.js.map +1 -1
  14. package/dist/components/PaymentFields/PaymentFields.d.ts +1 -2
  15. package/dist/components/PaymentFields/PaymentFields.js +1 -1
  16. package/dist/components/PaymentFields/PaymentFields.js.map +1 -1
  17. package/dist/components/PaymentForm/Messages.js +1 -1
  18. package/dist/components/PaymentForm/Messages.js.map +1 -1
  19. package/dist/components/PaymentForm/PaymentForm.d.ts +8 -2
  20. package/dist/components/PaymentForm/PaymentForm.js +14 -2
  21. package/dist/components/PaymentForm/PaymentForm.js.map +1 -1
  22. package/dist/components/PromoCode/Messages.d.ts +21 -1
  23. package/dist/components/PromoCode/Messages.js +25 -5
  24. package/dist/components/PromoCode/Messages.js.map +1 -1
  25. package/dist/components/PromoCode/PromoCode.d.ts +16 -1
  26. package/dist/components/PromoCode/PromoCode.js +15 -5
  27. package/dist/components/PromoCode/PromoCode.js.map +1 -1
  28. package/dist/data/hooks/useAlacarteCheckout.d.ts +13 -0
  29. package/dist/data/hooks/useAlacarteCheckout.js +41 -24
  30. package/dist/data/hooks/useAlacarteCheckout.js.map +1 -1
  31. package/dist/data/hooks/useCheckout.d.ts +3 -1
  32. package/dist/data/hooks/useCheckout.js +1 -0
  33. package/dist/data/hooks/useCheckout.js.map +1 -1
  34. package/dist/styles/components/cartSummary.scss +1 -1
  35. package/dist/styles/components/checkout.scss +9 -4
  36. package/dist/styles/components/expressCheckout.scss +6 -1
  37. package/dist/styles/components/paymentForm.scss +1 -1
  38. package/dist/styles/components/promoCode.scss +103 -52
  39. package/dist/types.d.ts +38 -1
  40. package/dist/types.js.map +1 -1
  41. package/dist/utils/discount.d.ts +7 -0
  42. package/dist/utils/discount.js +18 -0
  43. package/dist/utils/discount.js.map +1 -0
  44. package/dist/utils/provision.js +21 -3
  45. package/dist/utils/provision.js.map +1 -1
  46. package/dist/utils/stripeCard.d.ts +9 -0
  47. package/dist/utils/stripeCard.js +15 -0
  48. package/dist/utils/stripeCard.js.map +1 -1
  49. package/package.json +1 -1
@@ -41,16 +41,16 @@ const CartSummary = ({ productTitle, productBlurb, learnerCount: learnerCountPro
41
41
  .map((c) => c.name)
42
42
  .filter(Boolean)
43
43
  .join(', ');
44
+ const discount = (_d = alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.discount) !== null && _d !== void 0 ? _d : null;
44
45
  const deduction = isProgramLine && alacarteLine.kind === 'program'
45
46
  ? alacarteLine.deduction
46
47
  : null;
47
- const discount = (_d = alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.discount) !== null && _d !== void 0 ? _d : 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
50
  }))] }))] }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "p-0 py-3", children: [course ? (_jsx(CourseEnrollmentCard, { course: course, variant: "summary" })) : isProgramLine && 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
- }) })] })), discount && (_jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center p-0", "data-testid": "discount-row", children: [_jsx("span", { className: "text-gray-700", children: formatMessage(messages.discount) }), _jsx("span", { className: "text-gray-700", children: formatMessage(messages.discountAmount, {
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, {
54
54
  amount: discount.amount.toFixed(2),
55
55
  currency: discount.currency,
56
56
  }) })] })), _jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center p-0", children: [_jsx("span", { className: "cart-summary__total-label text-gray-900", children: formatMessage(messages.todaysTotal) }), _jsx("span", { className: "cart-summary__total-value mb-0 text-gray-900", children: totalText })] })] }), promoCode ? _jsx(PromoCode, Object.assign({}, promoCode)) : null, _jsxs("div", { className: "cart-summary__stats d-none d-lg-flex", children: [_jsxs("div", { className: "cart-summary__stat d-flex align-items-center mr-4", children: [_jsx("img", { src: learnersIcon, alt: "", width: 50, height: 50, className: "mr-2" }), _jsxs("div", { children: [_jsx("p", { className: "cart-summary__stat-count font-weight-bold text-gray-500 mb-0", children: formatMessage(messages.learnersCount) }), _jsx("p", { className: "cart-summary__stat-label text-gray-500 mb-0", children: formatMessage(messages.learnersLabel) })] })] }), _jsxs("div", { className: "cart-summary__stat d-flex align-items-center", children: [_jsx("img", { src: institutionsIcon, alt: "", width: 50, height: 50, className: "mr-2" }), _jsxs("div", { children: [_jsx("p", { className: "cart-summary__stat-count font-weight-bold text-gray-500 mb-0", children: formatMessage(messages.institutionsCount) }), _jsx("p", { className: "cart-summary__stat-label text-gray-500 mb-0", children: formatMessage(messages.institutionsLabel) })] })] })] })] }));
@@ -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 deduction = isProgramLine && alacarteLine.kind === 'program'\n ? alacarteLine.deduction\n : null;\n const discount = alacarteLine?.discount ?? 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-gray-700\">\n {formatMessage(messages.discount)}\n </span>\n <span className=\"text-gray-700\">\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 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"]}
@@ -49,22 +49,22 @@ declare const messages: {
49
49
  defaultMessage: string;
50
50
  description: string;
51
51
  };
52
- deduction: {
52
+ discountApplied: {
53
53
  id: string;
54
54
  defaultMessage: string;
55
55
  description: string;
56
56
  };
57
- deductionAmount: {
57
+ discountAmount: {
58
58
  id: string;
59
59
  defaultMessage: string;
60
60
  description: string;
61
61
  };
62
- discount: {
62
+ deduction: {
63
63
  id: string;
64
64
  defaultMessage: string;
65
65
  description: string;
66
66
  };
67
- discountAmount: {
67
+ deductionAmount: {
68
68
  id: string;
69
69
  defaultMessage: string;
70
70
  description: string;
@@ -51,6 +51,16 @@ const messages = defineMessages({
51
51
  defaultMessage: 'Already yours: {names}',
52
52
  description: 'Lists program member courses the learner already owns (explains the deduction)',
53
53
  },
54
+ discountApplied: {
55
+ id: 'checkout.cartSummary.discountApplied',
56
+ defaultMessage: 'Discounts applied',
57
+ description: 'Label for the coupon discount row shown below the subtotal',
58
+ },
59
+ discountAmount: {
60
+ id: 'checkout.cartSummary.discountAmount',
61
+ defaultMessage: '-${amount} {currency}',
62
+ description: 'Formatted discount amount e.g. -$122.50 USD',
63
+ },
54
64
  deduction: {
55
65
  id: 'checkout.cartSummary.deduction',
56
66
  defaultMessage: 'Already owned courses',
@@ -61,16 +71,6 @@ const messages = defineMessages({
61
71
  defaultMessage: '-${amount} {currency}',
62
72
  description: 'Formatted deduction amount e.g. -$100.00 USD',
63
73
  },
64
- discount: {
65
- id: 'checkout.cartSummary.discount',
66
- defaultMessage: 'Discount',
67
- description: 'Label for the coupon discount row',
68
- },
69
- discountAmount: {
70
- id: 'checkout.cartSummary.discountAmount',
71
- defaultMessage: '-${amount} {currency}',
72
- description: 'Formatted coupon discount amount e.g. -$10.00 USD',
73
- },
74
74
  learnersCount: {
75
75
  id: 'checkout.cartSummary.learnersCount',
76
76
  defaultMessage: '100M',
@@ -1 +1 @@
1
- {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/CartSummary/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,cAAc;IACd,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,oCAAoC;KAClD;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,8CAA8C;KAC5D;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,+DAA+D;QAC/E,WAAW,EAAE,oEAAoE;KAClF;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,0BAA0B;QAC1C,WAAW,EAAE,yCAAyC;KACvD;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,0BAA0B;QAC1C,WAAW,EAAE,oDAAoD;KAClE;IACD,QAAQ,EAAE;QACR,EAAE,EAAE,+BAA+B;QACnC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,4BAA4B;KAC1C;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,wDAAwD;KACtE;IACD,WAAW,EAAE;QACX,EAAE,EAAE,kCAAkC;QACtC,cAAc,EAAE,eAAe;QAC/B,WAAW,EAAE,mCAAmC;KACjD;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,6DAA6D;KAC3E;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,wBAAwB;QACxC,WAAW,EAAE,gFAAgF;KAC9F;IACD,SAAS,EAAE;QACT,EAAE,EAAE,gCAAgC;QACpC,cAAc,EAAE,uBAAuB;QACvC,WAAW,EAAE,gEAAgE;KAC9E;IACD,eAAe,EAAE;QACf,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,uBAAuB;QACvC,WAAW,EAAE,8CAA8C;KAC5D;IACD,QAAQ,EAAE;QACR,EAAE,EAAE,+BAA+B;QACnC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,mCAAmC;KACjD;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,uBAAuB;QACvC,WAAW,EAAE,mDAAmD;KACjE;IACD,aAAa,EAAE;QACb,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,qBAAqB;KACnC;IACD,aAAa,EAAE;QACb,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,qCAAqC;KACnD;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,gCAAgC;KAC9C;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,qBAAqB;QACrC,WAAW,EAAE,yCAAyC;KACvD;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n // CartSummary\n summaryHeading: {\n id: 'checkout.cartSummary.summaryHeading',\n defaultMessage: 'Summary',\n description: 'Heading for the order summary card',\n },\n productTitle: {\n id: 'checkout.cartSummary.productTitle',\n defaultMessage: 'edX Plus',\n description: 'Product name shown in the order summary card',\n },\n productBlurb: {\n id: 'checkout.cartSummary.productBlurb',\n defaultMessage: 'Subscribe and get access to hundreds of courses and programs.',\n description: 'Short product description under the name in the order summary card',\n },\n learnersSubscribed: {\n id: 'checkout.cartSummary.learnersSubscribed',\n defaultMessage: '{count} already enrolled',\n description: 'Badge showing number of active learners',\n },\n loadingSubscribers: {\n id: 'checkout.cartSummary.loadingSubscribers',\n defaultMessage: 'Loading subscriber count',\n description: 'Accessible label for the subscriber count skeleton',\n },\n subtotal: {\n id: 'checkout.cartSummary.subtotal',\n defaultMessage: 'Subtotal',\n description: 'Label for the subtotal row',\n },\n subtotalAmount: {\n id: 'checkout.cartSummary.subtotalAmount',\n defaultMessage: '${amount} {currency}',\n description: 'Formatted subtotal with currency code e.g. $219.00 USD',\n },\n todaysTotal: {\n id: 'checkout.cartSummary.todaysTotal',\n defaultMessage: \"Today's total\",\n description: \"Label for today's total price row\",\n },\n todaysTotalValue: {\n id: 'checkout.cartSummary.todaysTotalValue',\n defaultMessage: '${amount} {currency}',\n description: \"Formatted today's total with currency code e.g. $219.00 USD\",\n },\n alreadyYours: {\n id: 'checkout.cartSummary.alreadyYours',\n defaultMessage: 'Already yours: {names}',\n description: 'Lists program member courses the learner already owns (explains the deduction)',\n },\n deduction: {\n id: 'checkout.cartSummary.deduction',\n defaultMessage: 'Already owned courses',\n description: 'Label for the program price deduction for owned member courses',\n },\n deductionAmount: {\n id: 'checkout.cartSummary.deductionAmount',\n defaultMessage: '-${amount} {currency}',\n description: 'Formatted deduction amount e.g. -$100.00 USD',\n },\n discount: {\n id: 'checkout.cartSummary.discount',\n defaultMessage: 'Discount',\n description: 'Label for the coupon discount row',\n },\n discountAmount: {\n id: 'checkout.cartSummary.discountAmount',\n defaultMessage: '-${amount} {currency}',\n description: 'Formatted coupon discount amount e.g. -$10.00 USD',\n },\n learnersCount: {\n id: 'checkout.cartSummary.learnersCount',\n defaultMessage: '100M',\n description: 'Total learners stat',\n },\n learnersLabel: {\n id: 'checkout.cartSummary.learnersLabel',\n defaultMessage: 'Learners',\n description: 'Label under the learners count stat',\n },\n institutionsCount: {\n id: 'checkout.cartSummary.institutionsCount',\n defaultMessage: '250+',\n description: 'Expert institutions count stat',\n },\n institutionsLabel: {\n id: 'checkout.cartSummary.institutionsLabel',\n defaultMessage: 'Expert institutions',\n description: 'Label under the institutions count stat',\n },\n});\n\nexport default messages;\n"]}
1
+ {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/CartSummary/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,cAAc;IACd,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,oCAAoC;KAClD;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,8CAA8C;KAC5D;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,+DAA+D;QAC/E,WAAW,EAAE,oEAAoE;KAClF;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,0BAA0B;QAC1C,WAAW,EAAE,yCAAyC;KACvD;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,0BAA0B;QAC1C,WAAW,EAAE,oDAAoD;KAClE;IACD,QAAQ,EAAE;QACR,EAAE,EAAE,+BAA+B;QACnC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,4BAA4B;KAC1C;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,wDAAwD;KACtE;IACD,WAAW,EAAE;QACX,EAAE,EAAE,kCAAkC;QACtC,cAAc,EAAE,eAAe;QAC/B,WAAW,EAAE,mCAAmC;KACjD;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,6DAA6D;KAC3E;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,wBAAwB;QACxC,WAAW,EAAE,gFAAgF;KAC9F;IACD,eAAe,EAAE;QACf,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,mBAAmB;QACnC,WAAW,EAAE,4DAA4D;KAC1E;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,uBAAuB;QACvC,WAAW,EAAE,6CAA6C;KAC3D;IACD,SAAS,EAAE;QACT,EAAE,EAAE,gCAAgC;QACpC,cAAc,EAAE,uBAAuB;QACvC,WAAW,EAAE,gEAAgE;KAC9E;IACD,eAAe,EAAE;QACf,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,uBAAuB;QACvC,WAAW,EAAE,8CAA8C;KAC5D;IACD,aAAa,EAAE;QACb,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,qBAAqB;KACnC;IACD,aAAa,EAAE;QACb,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,qCAAqC;KACnD;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,gCAAgC;KAC9C;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,qBAAqB;QACrC,WAAW,EAAE,yCAAyC;KACvD;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n // CartSummary\n summaryHeading: {\n id: 'checkout.cartSummary.summaryHeading',\n defaultMessage: 'Summary',\n description: 'Heading for the order summary card',\n },\n productTitle: {\n id: 'checkout.cartSummary.productTitle',\n defaultMessage: 'edX Plus',\n description: 'Product name shown in the order summary card',\n },\n productBlurb: {\n id: 'checkout.cartSummary.productBlurb',\n defaultMessage: 'Subscribe and get access to hundreds of courses and programs.',\n description: 'Short product description under the name in the order summary card',\n },\n learnersSubscribed: {\n id: 'checkout.cartSummary.learnersSubscribed',\n defaultMessage: '{count} already enrolled',\n description: 'Badge showing number of active learners',\n },\n loadingSubscribers: {\n id: 'checkout.cartSummary.loadingSubscribers',\n defaultMessage: 'Loading subscriber count',\n description: 'Accessible label for the subscriber count skeleton',\n },\n subtotal: {\n id: 'checkout.cartSummary.subtotal',\n defaultMessage: 'Subtotal',\n description: 'Label for the subtotal row',\n },\n subtotalAmount: {\n id: 'checkout.cartSummary.subtotalAmount',\n defaultMessage: '${amount} {currency}',\n description: 'Formatted subtotal with currency code e.g. $219.00 USD',\n },\n todaysTotal: {\n id: 'checkout.cartSummary.todaysTotal',\n defaultMessage: \"Today's total\",\n description: \"Label for today's total price row\",\n },\n todaysTotalValue: {\n id: 'checkout.cartSummary.todaysTotalValue',\n defaultMessage: '${amount} {currency}',\n description: \"Formatted today's total with currency code e.g. $219.00 USD\",\n },\n alreadyYours: {\n id: 'checkout.cartSummary.alreadyYours',\n defaultMessage: 'Already yours: {names}',\n description: 'Lists program member courses the learner already owns (explains the deduction)',\n },\n discountApplied: {\n id: 'checkout.cartSummary.discountApplied',\n defaultMessage: 'Discounts applied',\n description: 'Label for the coupon discount row shown below the subtotal',\n },\n discountAmount: {\n id: 'checkout.cartSummary.discountAmount',\n defaultMessage: '-${amount} {currency}',\n description: 'Formatted discount amount e.g. -$122.50 USD',\n },\n deduction: {\n id: 'checkout.cartSummary.deduction',\n defaultMessage: 'Already owned courses',\n description: 'Label for the program price deduction for owned member courses',\n },\n deductionAmount: {\n id: 'checkout.cartSummary.deductionAmount',\n defaultMessage: '-${amount} {currency}',\n description: 'Formatted deduction amount e.g. -$100.00 USD',\n },\n learnersCount: {\n id: 'checkout.cartSummary.learnersCount',\n defaultMessage: '100M',\n description: 'Total learners stat',\n },\n learnersLabel: {\n id: 'checkout.cartSummary.learnersLabel',\n defaultMessage: 'Learners',\n description: 'Label under the learners count stat',\n },\n institutionsCount: {\n id: 'checkout.cartSummary.institutionsCount',\n defaultMessage: '250+',\n description: 'Expert institutions count stat',\n },\n institutionsLabel: {\n id: 'checkout.cartSummary.institutionsLabel',\n defaultMessage: 'Expert institutions',\n description: 'Label under the institutions count stat',\n },\n});\n\nexport default messages;\n"]}
@@ -1,3 +1,12 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  var __rest = (this && this.__rest) || function (s, e) {
2
11
  var t = {};
3
12
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
@@ -10,9 +19,9 @@ var __rest = (this && this.__rest) || function (s, e) {
10
19
  return t;
11
20
  };
12
21
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
- import { useEffect, useState } from 'react';
22
+ import { useCallback, useEffect, useState } from 'react';
14
23
  import { Alert, Col, Row } from '@openedx/paragon';
15
- import { Error as ErrorIcon } from '@openedx/paragon/icons';
24
+ import { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';
16
25
  import { useIntl } from '@openedx/frontend-base';
17
26
  import useCheckout from '../../data/hooks/useCheckout';
18
27
  import { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';
@@ -26,6 +35,8 @@ import AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';
26
35
  import AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel
27
36
  import { getTrialDateRange } from '../../utils';
28
37
  import { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';
38
+ import { appliedDiscountPercent } from '../../utils/discount';
39
+ import { confirmPaymentWithMethodId } from '../../utils/stripeCard';
29
40
  import { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';
30
41
  import messages from './Messages';
31
42
  // TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a
@@ -33,20 +44,40 @@ import messages from './Messages';
33
44
  // Flip back to true to restore the redirect.
34
45
  const REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;
35
46
  const Checkout = (_a) => {
36
- var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
47
+ var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
37
48
  var { homeUrl = '/', orderHistoryUrl } = _a, props = __rest(_a, ["homeUrl", "orderHistoryUrl"]);
38
49
  const { formatMessage } = useIntl();
39
50
  const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });
40
51
  const c = useCheckout(props);
41
52
  const [promoCodeValue, setPromoCodeValue] = useState('');
53
+ const [dismissedCouponAlert, setDismissedCouponAlert] = useState(null);
54
+ // Last code we successfully priced — drives the "Save {percent}% with code
55
+ // {code}" hint on the empty field while a coupon is not yet applied.
56
+ const [advertisedOffer, setAdvertisedOffer] = useState(null);
42
57
  const isAlacarteReady = alacarte.phase === 'ready' && !!alacarte.checkout;
58
+ const appliedCouponCode = ((_c = (_b = alacarte.checkout) === null || _b === void 0 ? void 0 : _b.couponCode) === null || _c === void 0 ? void 0 : _c.trim())
59
+ ? alacarte.checkout.couponCode.trim()
60
+ : null;
61
+ const discountPercent = appliedDiscountPercent(alacarte.line);
62
+ const appliedDiscount = appliedCouponCode && ((_d = alacarte.line) === null || _d === void 0 ? void 0 : _d.discount) && discountPercent != null
63
+ ? {
64
+ percent: discountPercent,
65
+ amount: alacarte.line.discount.amount,
66
+ currency: alacarte.line.discount.currency,
67
+ }
68
+ : null;
43
69
  // Keep the coupon field in sync when a code is applied via URL or API.
44
70
  useEffect(() => {
45
71
  var _a;
46
72
  if ((_a = alacarte.checkout) === null || _a === void 0 ? void 0 : _a.couponCode) {
47
73
  setPromoCodeValue(alacarte.checkout.couponCode);
48
74
  }
49
- }, [(_b = alacarte.checkout) === null || _b === void 0 ? void 0 : _b.couponCode]);
75
+ }, [(_e = alacarte.checkout) === null || _e === void 0 ? void 0 : _e.couponCode]);
76
+ useEffect(() => {
77
+ if (appliedCouponCode && discountPercent != null) {
78
+ setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });
79
+ }
80
+ }, [appliedCouponCode, discountPercent]);
50
81
  // No query params and no active cart — nothing to check out, send the
51
82
  // learner home rather than showing a placeholder.
52
83
  useEffect(() => {
@@ -54,6 +85,23 @@ const Checkout = (_a) => {
54
85
  window.location.href = homeUrl;
55
86
  }
56
87
  }, [alacarte.phase, homeUrl]);
88
+ // Express checkout (Apple Pay / Google Pay): runs the full place-order flow
89
+ // using a wallet-provided PaymentMethod id instead of the mounted card element.
90
+ const handleExpressPayment = useCallback((paymentMethodId, walletCountry) => __awaiter(void 0, void 0, void 0, function* () {
91
+ if (!c.stripe)
92
+ throw new Error('Stripe not loaded');
93
+ const stripeInstance = c.stripe;
94
+ yield alacarte.placeOrderAsync({
95
+ billing: Object.assign(Object.assign({}, c.billing), { country: c.billing.country || walletCountry || c.userCountry || '' }),
96
+ confirmCardPayment: (clientSecret) => __awaiter(void 0, void 0, void 0, function* () {
97
+ yield confirmPaymentWithMethodId({
98
+ stripe: stripeInstance,
99
+ clientSecret,
100
+ paymentMethodId,
101
+ });
102
+ }),
103
+ });
104
+ }), [alacarte, c.billing, c.userCountry, c.stripe]);
57
105
  const showProcessing = c.returningFromStripe
58
106
  && c.processingSubscription
59
107
  && c.phase !== 'success'
@@ -72,11 +120,11 @@ const Checkout = (_a) => {
72
120
  const selectedCycle = c.selectedCycle;
73
121
  // Prefer the à la carte course's own detail (skills/badge/image, richer than
74
122
  // the subscription flow's ?course_run_key= lookup) when we selected a course.
75
- const alacarteCourseDetail = ((_c = alacarte.line) === null || _c === void 0 ? void 0 : _c.kind) === 'course' && alacarte.productDetail && 'maId' in alacarte.productDetail
123
+ const alacarteCourseDetail = ((_f = alacarte.line) === null || _f === void 0 ? void 0 : _f.kind) === 'course' && alacarte.productDetail && 'maId' in alacarte.productDetail
76
124
  ? alacarte.productDetail
77
125
  : null;
78
126
  const courseForSummary = alacarteCourseDetail !== null && alacarteCourseDetail !== void 0 ? alacarteCourseDetail : c.course;
79
- const alacarteProgramDetail = ((_d = alacarte.line) === null || _d === void 0 ? void 0 : _d.kind) === 'program' && alacarte.productDetail && 'tCourses' in alacarte.productDetail
127
+ const alacarteProgramDetail = ((_g = alacarte.line) === null || _g === void 0 ? void 0 : _g.kind) === 'program' && alacarte.productDetail && 'tCourses' in alacarte.productDetail
80
128
  ? alacarte.productDetail
81
129
  : null;
82
130
  const handleStartCourse = () => {
@@ -111,9 +159,9 @@ const Checkout = (_a) => {
111
159
  // tPlacedOrder is still polling, keep the checkout form so Pay now can show
112
160
  // "Processing..." instead of a full-page loader.
113
161
  if (alacarte.placedOrder || alacarte.placedOrderError) {
114
- const confirmationTitle = ((_e = alacarte.line) === null || _e === void 0 ? void 0 : _e.kind) === 'program'
162
+ const confirmationTitle = ((_h = alacarte.line) === null || _h === void 0 ? void 0 : _h.kind) === 'program'
115
163
  ? alacarte.line.programName
116
- : ((_f = alacarte.line) === null || _f === void 0 ? void 0 : _f.kind) === 'course'
164
+ : ((_j = alacarte.line) === null || _j === void 0 ? void 0 : _j.kind) === 'course'
117
165
  ? alacarte.line.courseName
118
166
  : null;
119
167
  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 }) }) })] }));
@@ -129,11 +177,14 @@ const Checkout = (_a) => {
129
177
  if (alacarte.phase === 'alreadyOwned' || alacarte.phase === 'noActiveCheckout') {
130
178
  return (_jsxs(_Fragment, { children: [alacarteDebug, _jsx(AlacarteNotice, { messageId: alacarte.noticeMessageId })] }));
131
179
  }
132
- 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, 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" }), c.error && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, dismissible: true, onClose: c.clearError, children: c.error })), alacarte.placeOrderError && (_jsx(Alert, { variant: "warning", icon: ErrorIcon, dismissible: true, onClose: alacarte.clearPlaceOrderError, className: "mb-3", children: alacarte.placeOrderError })), !alacarte.priceAccepted && isAlacarteReady && ((_g = alacarte.line) === null || _g === void 0 ? void 0 : _g.amountDue) && (_jsx(Alert, { variant: "info", className: "mb-3", "data-testid": "price-accept-notice", children: formatMessage(messages.priceChangedNotice, {
180
+ 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, 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" }), c.error && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, dismissible: true, onClose: c.clearError, children: c.error })), alacarte.placeOrderError && (_jsx(Alert, { variant: "warning", icon: ErrorIcon, dismissible: true, onClose: alacarte.clearPlaceOrderError, className: "mb-3", children: alacarte.placeOrderError })), !alacarte.priceAccepted && isAlacarteReady && ((_k = alacarte.line) === null || _k === void 0 ? void 0 : _k.amountDue) && (_jsx(Alert, { variant: "info", className: "mb-3", "data-testid": "price-accept-notice", children: formatMessage(messages.priceChangedNotice, {
133
181
  amount: alacarte.line.amountDue.amount.toFixed(2),
134
182
  currency: alacarte.line.amountDue.currency,
135
183
  }) })), showForm && (_jsxs(_Fragment, { children: [c.phase === 'failed' && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, className: "mb-3", children: failedMessage
136
- || formatMessage(messages.paymentProcessingDescription) })), _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
184
+ || 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
+ code: appliedCouponCode,
186
+ 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
137
188
  ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)
138
189
  : c.processingSubscription, onPlaceOrder: isAlacarteReady
139
190
  ? () => {
@@ -145,7 +196,7 @@ const Checkout = (_a) => {
145
196
  confirmCardPayment: c.confirmCardPayment,
146
197
  });
147
198
  }
148
- : c.placeOrder, trialEnd: selectedCycle === 'monthly' ? displayEnd : undefined, priceToShow: null, cycleToShow: selectedCycle === 'monthly' ? 'monthly' : 'annual', userCountry: c.userCountry, geolocationUrl: props.geolocationUrl, alacarteLine: alacarte.line })] })), c.phase === 'success' && (_jsx(SuccessScreen, { subscriptionBiId: ((_h = c.subscriptionDetails) === null || _h === void 0 ? void 0 : _h.biId) || '', cardLast4: ((_j = c.subscriptionDetails) === null || _j === void 0 ? void 0 : _j.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, promoCode: isAlacarteReady ? {
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: ((_l = c.subscriptionDetails) === null || _l === void 0 ? void 0 : _l.biId) || '', cardLast4: ((_m = c.subscriptionDetails) === null || _m === void 0 ? void 0 : _m.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, promoCode: isAlacarteReady ? {
149
200
  value: promoCodeValue,
150
201
  onChange: (next) => {
151
202
  setPromoCodeValue(next);
@@ -156,8 +207,11 @@ const Checkout = (_a) => {
156
207
  onRemove: () => {
157
208
  alacarte.removeCoupon();
158
209
  setPromoCodeValue('');
210
+ setAdvertisedOffer(null);
159
211
  },
160
- appliedCode: (_l = (_k = alacarte.checkout) === null || _k === void 0 ? void 0 : _k.couponCode) !== null && _l !== void 0 ? _l : null,
212
+ appliedCode: appliedCouponCode,
213
+ discount: appliedDiscount,
214
+ offer: !appliedCouponCode ? advertisedOffer : null,
161
215
  error: alacarte.couponError,
162
216
  isApplying: alacarte.couponApplying,
163
217
  disabled: alacarte.placingOrder,
@@ -1 +1 @@
1
- {"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useState } from 'react';\nimport { Alert, Col, Row } from '@openedx/paragon';\nimport { 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 { 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 isAlacarteReady = alacarte.phase === 'ready' && !!alacarte.checkout;\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 // 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 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 <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 />\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 },\n appliedCode: alacarte.checkout?.couponCode ?? 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, 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"]}
@@ -9,5 +9,15 @@ declare const messages: {
9
9
  defaultMessage: string;
10
10
  description: string;
11
11
  };
12
+ couponAppliedAlert: {
13
+ id: string;
14
+ defaultMessage: string;
15
+ description: string;
16
+ };
17
+ dismiss: {
18
+ id: string;
19
+ defaultMessage: string;
20
+ description: string;
21
+ };
12
22
  };
13
23
  export default messages;
@@ -10,6 +10,16 @@ const messages = defineMessages({
10
10
  defaultMessage: 'The price is now ${amount} {currency}. Click Pay now to accept this amount and continue.',
11
11
  description: 'Shown when checkout.priceAccepted is false so the learner confirms the current amount due',
12
12
  },
13
+ couponAppliedAlert: {
14
+ id: 'checkout.couponAppliedAlert',
15
+ defaultMessage: 'Coupon code {code} applied for {percent}% off!',
16
+ description: 'Success alert shown above payment methods after a coupon is applied',
17
+ },
18
+ dismiss: {
19
+ id: 'checkout.couponAppliedAlert.dismiss',
20
+ defaultMessage: 'Dismiss',
21
+ description: 'Closes the coupon-applied success alert',
22
+ },
13
23
  });
14
24
  export default messages;
15
25
  //# 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;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});\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;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,2 +1,21 @@
1
- declare const ExpressCheckout: () => import("react/jsx-runtime").JSX.Element;
1
+ import React from 'react';
2
+ import type { StripeLike } from '../../types';
3
+ export interface ExpressCheckoutProps {
4
+ /** Stripe.js instance — null until loaded. */
5
+ stripe: StripeLike | null;
6
+ /** Total amount in the smallest currency unit (cents). */
7
+ totalAmountCents: number | null;
8
+ /** ISO 4217 currency code, e.g. 'USD'. */
9
+ currency: string | null;
10
+ /** Two-letter country code for Payment Request initialisation. */
11
+ country: string;
12
+ /**
13
+ * Called when the user authorises a wallet payment (Apple Pay / Google Pay).
14
+ * Receives the Stripe PaymentMethod id and the billing country from the wallet.
15
+ * Must resolve on success or reject on failure.
16
+ */
17
+ onPaymentAuthorized: (paymentMethodId: string, walletCountry?: string) => Promise<void>;
18
+ disabled?: boolean;
19
+ }
20
+ declare const ExpressCheckout: React.FC<ExpressCheckoutProps>;
2
21
  export default ExpressCheckout;