@edx/frontend-app-subscription-manage 1.0.1 → 1.2.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 (131) hide show
  1. package/dist/Main.js +1 -1
  2. package/dist/Main.js.map +1 -1
  3. package/dist/app.js +3 -0
  4. package/dist/app.js.map +1 -1
  5. package/dist/checkoutPage/CheckoutPage.d.ts +3 -0
  6. package/dist/checkoutPage/CheckoutPage.js +24 -0
  7. package/dist/checkoutPage/CheckoutPage.js.map +1 -0
  8. package/dist/components/BrandMarks/BrandMarks.d.ts +3 -0
  9. package/dist/components/BrandMarks/BrandMarks.js +9 -0
  10. package/dist/components/BrandMarks/BrandMarks.js.map +1 -0
  11. package/dist/components/CartSummary/CartSummary.d.ts +21 -0
  12. package/dist/components/CartSummary/CartSummary.js +30 -0
  13. package/dist/components/CartSummary/CartSummary.js.map +1 -0
  14. package/dist/components/CartSummary/Messages.d.ts +73 -0
  15. package/dist/components/CartSummary/Messages.js +76 -0
  16. package/dist/components/CartSummary/Messages.js.map +1 -0
  17. package/dist/components/Checkout/Checkout.d.ts +4 -0
  18. package/dist/components/Checkout/Checkout.js +37 -0
  19. package/dist/components/Checkout/Checkout.js.map +1 -0
  20. package/dist/components/CheckoutGate/CheckoutGate.d.ts +9 -0
  21. package/dist/components/CheckoutGate/CheckoutGate.js +34 -0
  22. package/dist/components/CheckoutGate/CheckoutGate.js.map +1 -0
  23. package/dist/components/CourseEnrollmentCard/CourseEnrollmentCard.d.ts +9 -0
  24. package/dist/components/CourseEnrollmentCard/CourseEnrollmentCard.js +28 -0
  25. package/dist/components/CourseEnrollmentCard/CourseEnrollmentCard.js.map +1 -0
  26. package/dist/components/CourseEnrollmentCard/Messages.d.ts +33 -0
  27. package/dist/components/CourseEnrollmentCard/Messages.js +35 -0
  28. package/dist/components/CourseEnrollmentCard/Messages.js.map +1 -0
  29. package/dist/components/EdxUnlimitedBanner/EdxUnlimitedBanner.d.ts +13 -0
  30. package/dist/components/EdxUnlimitedBanner/EdxUnlimitedBanner.js +11 -0
  31. package/dist/components/EdxUnlimitedBanner/EdxUnlimitedBanner.js.map +1 -0
  32. package/dist/components/EnrollmentSummary/EnrollmentSummary.d.ts +15 -0
  33. package/dist/components/EnrollmentSummary/EnrollmentSummary.js +21 -0
  34. package/dist/components/EnrollmentSummary/EnrollmentSummary.js.map +1 -0
  35. package/dist/components/EnrollmentSummary/Messages.d.ts +33 -0
  36. package/dist/components/EnrollmentSummary/Messages.js +35 -0
  37. package/dist/components/EnrollmentSummary/Messages.js.map +1 -0
  38. package/dist/components/PaymentFields/PaymentFields.d.ts +9 -0
  39. package/dist/components/PaymentFields/PaymentFields.js +7 -0
  40. package/dist/components/PaymentFields/PaymentFields.js.map +1 -0
  41. package/dist/components/PaymentForm/Messages.d.ts +88 -0
  42. package/dist/components/PaymentForm/Messages.js +95 -0
  43. package/dist/components/PaymentForm/Messages.js.map +1 -0
  44. package/dist/components/PaymentForm/PaymentForm.d.ts +21 -0
  45. package/dist/components/PaymentForm/PaymentForm.js +69 -0
  46. package/dist/components/PaymentForm/PaymentForm.js.map +1 -0
  47. package/dist/components/StatusScreens/Messages.d.ts +103 -0
  48. package/dist/components/StatusScreens/Messages.js +108 -0
  49. package/dist/components/StatusScreens/Messages.js.map +1 -0
  50. package/dist/components/StatusScreens/StatusScreens.d.ts +23 -0
  51. package/dist/components/StatusScreens/StatusScreens.js +25 -0
  52. package/dist/components/StatusScreens/StatusScreens.js.map +1 -0
  53. package/dist/constants.d.ts +59 -0
  54. package/dist/constants.js +275 -0
  55. package/dist/constants.js.map +1 -1
  56. package/dist/data/hooks/keys.d.ts +14 -0
  57. package/dist/data/hooks/keys.js +15 -0
  58. package/dist/data/hooks/keys.js.map +1 -0
  59. package/dist/data/hooks/useCheckout.d.ts +57 -0
  60. package/dist/data/hooks/useCheckout.js +249 -0
  61. package/dist/data/hooks/useCheckout.js.map +1 -0
  62. package/dist/data/hooks/useCheckoutGate.d.ts +15 -0
  63. package/dist/data/hooks/useCheckoutGate.js +54 -0
  64. package/dist/data/hooks/useCheckoutGate.js.map +1 -0
  65. package/dist/data/hooks/useCheckoutMutations.d.ts +16 -0
  66. package/dist/data/hooks/useCheckoutMutations.js +131 -0
  67. package/dist/data/hooks/useCheckoutMutations.js.map +1 -0
  68. package/dist/data/hooks/useCheckoutQueries.d.ts +35 -0
  69. package/dist/data/hooks/useCheckoutQueries.js +110 -0
  70. package/dist/data/hooks/useCheckoutQueries.js.map +1 -0
  71. package/dist/data/services/subs/urls.d.ts +2 -0
  72. package/dist/data/services/subs/urls.js +4 -0
  73. package/dist/data/services/subs/urls.js.map +1 -0
  74. package/dist/routes.d.ts +2 -1
  75. package/dist/routes.js +33 -4
  76. package/dist/routes.js.map +1 -1
  77. package/dist/style.scss +13 -0
  78. package/dist/styles/components/brandMarks.scss +54 -0
  79. package/dist/styles/components/cartSummary.scss +18 -0
  80. package/dist/styles/components/checkout.scss +29 -0
  81. package/dist/styles/components/checkoutStepper.scss +9 -0
  82. package/dist/styles/components/courseEnrollmentCard.scss +38 -0
  83. package/dist/styles/components/edxUnlimitedBanner.scss +138 -0
  84. package/dist/styles/components/enrollmentSummary.scss +13 -0
  85. package/dist/styles/components/paymentFields.scss +15 -0
  86. package/dist/styles/components/paymentForm.scss +105 -0
  87. package/dist/styles/components/statusScreens.scss +29 -0
  88. package/dist/styles/components/successScreen.scss +0 -0
  89. package/dist/types.d.ts +222 -0
  90. package/dist/types.js +2 -0
  91. package/dist/types.js.map +1 -0
  92. package/dist/utils/auth.d.ts +7 -0
  93. package/dist/utils/auth.js +28 -0
  94. package/dist/utils/auth.js.map +1 -0
  95. package/dist/utils/billingDetails.d.ts +30 -0
  96. package/dist/utils/billingDetails.js +47 -0
  97. package/dist/utils/billingDetails.js.map +1 -0
  98. package/dist/utils/bootstrapApi.d.ts +20 -0
  99. package/dist/utils/bootstrapApi.js +142 -0
  100. package/dist/utils/bootstrapApi.js.map +1 -0
  101. package/dist/utils/cartApi.d.ts +16 -0
  102. package/dist/utils/cartApi.js +118 -0
  103. package/dist/utils/cartApi.js.map +1 -0
  104. package/dist/utils/checkout.d.ts +6 -0
  105. package/dist/utils/checkout.js +28 -0
  106. package/dist/utils/checkout.js.map +1 -0
  107. package/dist/utils/fetchActiveSubscriptions.d.ts +4 -0
  108. package/dist/utils/fetchActiveSubscriptions.js +36 -0
  109. package/dist/utils/fetchActiveSubscriptions.js.map +1 -0
  110. package/dist/utils/fetchBillingPlans.d.ts +2 -0
  111. package/dist/utils/fetchBillingPlans.js +48 -0
  112. package/dist/utils/fetchBillingPlans.js.map +1 -0
  113. package/dist/utils/graphqlClient.d.ts +16 -0
  114. package/dist/utils/graphqlClient.js +39 -0
  115. package/dist/utils/graphqlClient.js.map +1 -0
  116. package/dist/utils/index.d.ts +3 -0
  117. package/dist/utils/index.js +17 -0
  118. package/dist/utils/index.js.map +1 -0
  119. package/dist/utils/provision.d.ts +25 -0
  120. package/dist/utils/provision.js +107 -0
  121. package/dist/utils/provision.js.map +1 -0
  122. package/dist/utils/queries.d.ts +35 -0
  123. package/dist/utils/queries.js +168 -0
  124. package/dist/utils/queries.js.map +1 -0
  125. package/dist/utils/stripe.d.ts +7 -0
  126. package/dist/utils/stripe.js +43 -0
  127. package/dist/utils/stripe.js.map +1 -0
  128. package/package.json +2 -2
  129. package/dist/example/ExamplePage.d.ts +0 -2
  130. package/dist/example/ExamplePage.js +0 -6
  131. package/dist/example/ExamplePage.js.map +0 -1
@@ -0,0 +1,35 @@
1
+ import { defineMessages } from '@openedx/frontend-base';
2
+ const messages = defineMessages({
3
+ checkoutEnrollCopy: {
4
+ id: 'checkout.courseEnrollmentCard.checkoutEnrollCopy',
5
+ defaultMessage: "After checkout you'll be enrolled in {brand}: {courseName}.",
6
+ description: 'Enrollment preview copy on the checkout summary sidebar',
7
+ },
8
+ checkoutEnrollCopyNoBrand: {
9
+ id: 'checkout.courseEnrollmentCard.checkoutEnrollCopyNoBrand',
10
+ defaultMessage: "After checkout you'll be enrolled in {courseName}.",
11
+ description: 'Enrollment preview copy when partner brand is unavailable',
12
+ },
13
+ successCourseLabel: {
14
+ id: 'checkout.courseEnrollmentCard.successCourseLabel',
15
+ defaultMessage: '{brand}: {courseName}.',
16
+ description: 'Course label on the post-checkout enrollment card',
17
+ },
18
+ successCourseLabelNoBrand: {
19
+ id: 'checkout.courseEnrollmentCard.successCourseLabelNoBrand',
20
+ defaultMessage: '{courseName}.',
21
+ description: 'Course label on the post-checkout card when partner brand is unavailable',
22
+ },
23
+ startCourse: {
24
+ id: 'checkout.courseEnrollmentCard.startCourse',
25
+ defaultMessage: 'Start course',
26
+ description: 'CTA to open the enrolled course after checkout',
27
+ },
28
+ courseImageAlt: {
29
+ id: 'checkout.courseEnrollmentCard.courseImageAlt',
30
+ defaultMessage: 'Course image for {courseName}',
31
+ description: 'Alt text for the course card thumbnail',
32
+ },
33
+ });
34
+ export default messages;
35
+ //# sourceMappingURL=Messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/CourseEnrollmentCard/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,kBAAkB,EAAE;QAClB,EAAE,EAAE,kDAAkD;QACtD,cAAc,EAAE,6DAA6D;QAC7E,WAAW,EAAE,yDAAyD;KACvE;IACD,yBAAyB,EAAE;QACzB,EAAE,EAAE,yDAAyD;QAC7D,cAAc,EAAE,oDAAoD;QACpE,WAAW,EAAE,2DAA2D;KACzE;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,kDAAkD;QACtD,cAAc,EAAE,wBAAwB;QACxC,WAAW,EAAE,mDAAmD;KACjE;IACD,yBAAyB,EAAE;QACzB,EAAE,EAAE,yDAAyD;QAC7D,cAAc,EAAE,eAAe;QAC/B,WAAW,EAAE,0EAA0E;KACxF;IACD,WAAW,EAAE;QACX,EAAE,EAAE,2CAA2C;QAC/C,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,gDAAgD;KAC9D;IACD,cAAc,EAAE;QACd,EAAE,EAAE,8CAA8C;QAClD,cAAc,EAAE,+BAA+B;QAC/C,WAAW,EAAE,wCAAwC;KACtD;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n checkoutEnrollCopy: {\n id: 'checkout.courseEnrollmentCard.checkoutEnrollCopy',\n defaultMessage: \"After checkout you'll be enrolled in {brand}: {courseName}.\",\n description: 'Enrollment preview copy on the checkout summary sidebar',\n },\n checkoutEnrollCopyNoBrand: {\n id: 'checkout.courseEnrollmentCard.checkoutEnrollCopyNoBrand',\n defaultMessage: \"After checkout you'll be enrolled in {courseName}.\",\n description: 'Enrollment preview copy when partner brand is unavailable',\n },\n successCourseLabel: {\n id: 'checkout.courseEnrollmentCard.successCourseLabel',\n defaultMessage: '{brand}: {courseName}.',\n description: 'Course label on the post-checkout enrollment card',\n },\n successCourseLabelNoBrand: {\n id: 'checkout.courseEnrollmentCard.successCourseLabelNoBrand',\n defaultMessage: '{courseName}.',\n description: 'Course label on the post-checkout card when partner brand is unavailable',\n },\n startCourse: {\n id: 'checkout.courseEnrollmentCard.startCourse',\n defaultMessage: 'Start course',\n description: 'CTA to open the enrolled course after checkout',\n },\n courseImageAlt: {\n id: 'checkout.courseEnrollmentCard.courseImageAlt',\n defaultMessage: 'Course image for {courseName}',\n description: 'Alt text for the course card thumbnail',\n },\n});\n\nexport default messages;\n"]}
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ interface EdxUnlimitedBannerProps {
3
+ headlinePre?: string;
4
+ headlinePost?: string;
5
+ body?: string;
6
+ ctaLabel?: string;
7
+ ctaHref?: string;
8
+ imageUrl?: string;
9
+ imageAlt?: string;
10
+ setShowLandingPage?: (show: boolean) => void;
11
+ }
12
+ declare const EdxUnlimitedBanner: React.FC<EdxUnlimitedBannerProps>;
13
+ export default EdxUnlimitedBanner;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const EdxUnlimitedBadge = () => (_jsxs("span", { className: "edx-banner__badge", "aria-label": "edX unlimited", children: [_jsx("span", { className: "edx-banner__badge-edx", children: "edX" }), _jsx("span", { className: "edx-banner__badge-unlimited", children: "unlimited" })] }));
3
+ const BulletIcon = () => (_jsx("span", { className: "edx-banner__bullet", "aria-hidden": "true", children: _jsxs("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", children: [_jsx("circle", { cx: "6", cy: "6", r: "3", fill: "#fff" }), _jsx("circle", { cx: "6", cy: "1.5", r: "1.5", fill: "#fff" }), _jsx("circle", { cx: "6", cy: "10.5", r: "1.5", fill: "#fff" }), _jsx("circle", { cx: "1.5", cy: "6", r: "1.5", fill: "#fff" }), _jsx("circle", { cx: "10.5", cy: "6", r: "1.5", fill: "#fff" })] }) }));
4
+ const EdxUnlimitedBanner = ({ headlinePre = 'Never stop learning with', headlinePost = '', body = 'Access 850 courses and programs from world-class institutions — all included for $39 a month. Expand your skills, grow your career, and keep learning for life.', ctaLabel = 'Start 7-day free trial', ctaHref = '#', imageUrl, imageAlt = 'Student learning online', setShowLandingPage, }) => (_jsxs("div", { className: "edx-banner", children: [_jsxs("div", { className: "edx-banner__content", children: [_jsxs("div", { className: "edx-banner__eyebrow", children: [_jsx(BulletIcon, {}), _jsxs("h2", { className: "edx-banner__headline", children: [headlinePre, " ", _jsx(EdxUnlimitedBadge, {}), headlinePost && ` ${headlinePost}`] })] }), _jsx("p", { className: "edx-banner__body", children: body }), _jsx("div", { className: "edx-banner__cta-wrapper", children: _jsx("a", { href: ctaHref, className: "edx-banner__cta", onClick: setShowLandingPage
5
+ ? (e) => {
6
+ e.preventDefault();
7
+ setShowLandingPage(false);
8
+ }
9
+ : undefined, children: ctaLabel }) })] }), imageUrl && (_jsx("div", { className: "edx-banner__photo", "aria-hidden": "true", children: _jsx("img", { src: imageUrl, alt: imageAlt }) })), !imageUrl && (_jsx("div", { className: "edx-banner__placeholder", "aria-hidden": "true", children: _jsxs("svg", { width: "48", height: "48", viewBox: "0 0 48 48", fill: "none", opacity: "0.3", children: [_jsx("rect", { x: "8", y: "8", width: "32", height: "32", rx: "16", stroke: "#fff", strokeWidth: "2" }), _jsx("path", { d: "M24 20a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM12 36c0-6.627 5.373-12 12-12s12 5.373 12 12", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round" })] }) }))] }));
10
+ export default EdxUnlimitedBanner;
11
+ //# sourceMappingURL=EdxUnlimitedBanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EdxUnlimitedBanner.js","sourceRoot":"","sources":["../../../src/components/EdxUnlimitedBanner/EdxUnlimitedBanner.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\n\ninterface EdxUnlimitedBannerProps {\n headlinePre?: string,\n headlinePost?: string,\n body?: string,\n ctaLabel?: string,\n ctaHref?: string,\n imageUrl?: string,\n imageAlt?: string,\n setShowLandingPage?: (show: boolean) => void,\n}\n\nconst EdxUnlimitedBadge = () => (\n <span className=\"edx-banner__badge\" aria-label=\"edX unlimited\">\n <span className=\"edx-banner__badge-edx\">edX</span>\n <span className=\"edx-banner__badge-unlimited\">unlimited</span>\n </span>\n);\n\nconst BulletIcon = () => (\n <span className=\"edx-banner__bullet\" aria-hidden=\"true\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <circle cx=\"6\" cy=\"6\" r=\"3\" fill=\"#fff\" />\n <circle cx=\"6\" cy=\"1.5\" r=\"1.5\" fill=\"#fff\" />\n <circle cx=\"6\" cy=\"10.5\" r=\"1.5\" fill=\"#fff\" />\n <circle cx=\"1.5\" cy=\"6\" r=\"1.5\" fill=\"#fff\" />\n <circle cx=\"10.5\" cy=\"6\" r=\"1.5\" fill=\"#fff\" />\n </svg>\n </span>\n);\n\nconst EdxUnlimitedBanner: React.FC<EdxUnlimitedBannerProps> = ({\n headlinePre = 'Never stop learning with',\n headlinePost = '',\n body = 'Access 850 courses and programs from world-class institutions — all included for $39 a month. Expand your skills, grow your career, and keep learning for life.',\n ctaLabel = 'Start 7-day free trial',\n ctaHref = '#',\n imageUrl,\n imageAlt = 'Student learning online',\n setShowLandingPage,\n}) => (\n <div className=\"edx-banner\">\n {/* ── Left: text content ── */}\n <div className=\"edx-banner__content\">\n <div className=\"edx-banner__eyebrow\">\n <BulletIcon />\n <h2 className=\"edx-banner__headline\">\n {headlinePre} <EdxUnlimitedBadge />\n {headlinePost && ` ${headlinePost}`}\n </h2>\n </div>\n\n <p className=\"edx-banner__body\">{body}</p>\n\n <div className=\"edx-banner__cta-wrapper\">\n <a\n href={ctaHref}\n className=\"edx-banner__cta\"\n onClick={\n setShowLandingPage\n ? (e) => {\n e.preventDefault();\n setShowLandingPage(false);\n }\n : undefined\n }\n >\n {ctaLabel}\n </a>\n </div>\n </div>\n\n {/* ── Right: photo ── */}\n {imageUrl && (\n <div className=\"edx-banner__photo\" aria-hidden=\"true\">\n <img src={imageUrl} alt={imageAlt} />\n </div>\n )}\n\n {/* Placeholder when no imageUrl */}\n {!imageUrl && (\n <div className=\"edx-banner__placeholder\" aria-hidden=\"true\">\n <svg\n width=\"48\"\n height=\"48\"\n viewBox=\"0 0 48 48\"\n fill=\"none\"\n opacity=\"0.3\"\n >\n <rect\n x=\"8\"\n y=\"8\"\n width=\"32\"\n height=\"32\"\n rx=\"16\"\n stroke=\"#fff\"\n strokeWidth=\"2\"\n />\n <path\n d=\"M24 20a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM12 36c0-6.627 5.373-12 12-12s12 5.373 12 12\"\n stroke=\"#fff\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </div>\n )}\n </div>\n);\n\nexport default EdxUnlimitedBanner;\n"]}
@@ -0,0 +1,15 @@
1
+ import type { CourseDetail } from '../../types';
2
+ export interface EnrollmentSummaryProps {
3
+ productTitle?: string;
4
+ productBlurb?: string;
5
+ /** e.g. "39" */
6
+ amount?: string;
7
+ /** e.g. "USD" */
8
+ currency?: string;
9
+ cycleToShow?: string;
10
+ /** When set, shows the enrolled course card below the purchase summary. */
11
+ course?: CourseDetail | null;
12
+ onStartCourse?: () => void;
13
+ }
14
+ declare const EnrollmentSummary: ({ productTitle, productBlurb, amount, currency, cycleToShow, course, onStartCourse, }: EnrollmentSummaryProps) => import("react/jsx-runtime").JSX.Element;
15
+ export default EnrollmentSummary;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Card } from '@openedx/paragon';
3
+ import { useIntl } from '@openedx/frontend-base';
4
+ import { EdxUnlimitedLogo } from '../BrandMarks/BrandMarks';
5
+ import CourseEnrollmentCard from '../CourseEnrollmentCard/CourseEnrollmentCard';
6
+ import messages from './Messages';
7
+ const EnrollmentSummary = ({ productTitle = 'edX Subscription', productBlurb = 'Subscribe and get access to hundreds of courses and programs.', amount, currency = 'USD', cycleToShow = 'monthly', course = null, onStartCourse, }) => {
8
+ const { formatMessage } = useIntl();
9
+ const isMonthly = cycleToShow === 'monthly';
10
+ const frequency = isMonthly ? 'month' : 'year';
11
+ const cycleLabel = isMonthly
12
+ ? formatMessage(messages.monthlySubscription)
13
+ : formatMessage(messages.annualSubscription);
14
+ return (_jsxs("div", { className: "enrollment-summary", children: [_jsxs(Card, { className: "enrollment-summary", children: [_jsxs(Card.Section, { children: [_jsx("p", { className: "font-weight-bold mb-3", children: formatMessage(messages.purchaseSummary) }), _jsxs("div", { className: "d-flex align-items-start mb-3", children: [_jsx(EdxUnlimitedLogo, {}), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-1", style: { fontSize: '0.9rem' }, children: productTitle }), _jsx("p", { className: "text-muted mb-0", style: { fontSize: '0.82rem' }, children: productBlurb })] })] }), _jsxs("div", { className: "d-flex justify-content-between align-items-center mb-1", children: [_jsx("span", { className: "small text-muted", children: cycleLabel }), amount && (_jsx("span", { className: "small font-weight-bold", children: formatMessage(messages.priceDisplay, {
15
+ amount,
16
+ frequency,
17
+ currency,
18
+ }) }))] })] }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center", children: [_jsx("span", { className: "font-weight-bold", children: formatMessage(messages.todaysTotal) }), _jsx("span", { className: "font-weight-bold text-success", children: formatMessage(messages.todaysTotalValue) })] })] }), course && (_jsx(CourseEnrollmentCard, { course: course, variant: "success", onStartCourse: onStartCourse }))] }));
19
+ };
20
+ export default EnrollmentSummary;
21
+ //# sourceMappingURL=EnrollmentSummary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnrollmentSummary.js","sourceRoot":"","sources":["../../../src/components/EnrollmentSummary/EnrollmentSummary.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,oBAAoB,MAAM,8CAA8C,CAAC;AAEhF,OAAO,QAAQ,MAAM,YAAY,CAAC;AAelC,MAAM,iBAAiB,GAAG,CAAC,EACzB,YAAY,GAAG,kBAAkB,EACjC,YAAY,GAAG,+DAA+D,EAC9E,MAAM,EACN,QAAQ,GAAG,KAAK,EAChB,WAAW,GAAG,SAAS,EACvB,MAAM,GAAG,IAAI,EACb,aAAa,GACU,EAAE,EAAE;IAC3B,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IAEpC,MAAM,SAAS,GAAG,WAAW,KAAK,SAAS,CAAC;IAC5C,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IAC/C,MAAM,UAAU,GAAG,SAAS;QAC1B,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAC7C,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAE/C,OAAO,CACL,eAAK,SAAS,EAAC,oBAAoB,aACjC,MAAC,IAAI,IAAC,SAAS,EAAC,oBAAoB,aAClC,MAAC,IAAI,CAAC,OAAO,eACX,YAAG,SAAS,EAAC,uBAAuB,YACjC,aAAa,CAAC,QAAQ,CAAC,eAAe,CAAC,GACtC,EAEJ,eAAK,SAAS,EAAC,+BAA+B,aAC5C,KAAC,gBAAgB,KAAG,EACpB,0BACE,YAAG,SAAS,EAAC,uBAAuB,EAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAC/D,YAAY,GACX,EACJ,YAAG,SAAS,EAAC,iBAAiB,EAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,YAC1D,YAAY,GACX,IACA,IACF,EAEN,eAAK,SAAS,EAAC,wDAAwD,aACrE,eAAM,SAAS,EAAC,kBAAkB,YAAE,UAAU,GAAQ,EACrD,MAAM,IAAI,CACT,eAAM,SAAS,EAAC,wBAAwB,YACrC,aAAa,CAAC,QAAQ,CAAC,YAAY,EAAE;4CACpC,MAAM;4CACN,SAAS;4CACT,QAAQ;yCACT,CAAC,GACG,CACR,IACG,IACO,EAEf,KAAC,IAAI,CAAC,OAAO,KAAG,EAEhB,MAAC,IAAI,CAAC,OAAO,IAAC,SAAS,EAAC,mDAAmD,aACzE,eAAM,SAAS,EAAC,kBAAkB,YAC/B,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,GAC/B,EACP,eAAM,SAAS,EAAC,+BAA+B,YAC5C,aAAa,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GACpC,IACM,IACV,EAEN,MAAM,IAAI,CACT,KAAC,oBAAoB,IACnB,MAAM,EAAE,MAAM,EACd,OAAO,EAAC,SAAS,EACjB,aAAa,EAAE,aAAa,GAC5B,CACH,IACG,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,iBAAiB,CAAC","sourcesContent":["import React from 'react';\nimport { Card } from '@openedx/paragon';\nimport { useIntl } from '@openedx/frontend-base';\nimport { EdxUnlimitedLogo } from '../BrandMarks/BrandMarks';\nimport CourseEnrollmentCard from '../CourseEnrollmentCard/CourseEnrollmentCard';\nimport type { CourseDetail } from '../../types';\nimport messages from './Messages';\n\nexport interface EnrollmentSummaryProps {\n productTitle?: string,\n productBlurb?: string,\n /** e.g. \"39\" */\n amount?: string,\n /** e.g. \"USD\" */\n currency?: string,\n cycleToShow?: string,\n /** When set, shows the enrolled course card below the purchase summary. */\n course?: CourseDetail | null,\n onStartCourse?: () => void,\n}\n\nconst EnrollmentSummary = ({\n productTitle = 'edX Subscription',\n productBlurb = 'Subscribe and get access to hundreds of courses and programs.',\n amount,\n currency = 'USD',\n cycleToShow = 'monthly',\n course = null,\n onStartCourse,\n}: EnrollmentSummaryProps) => {\n const { formatMessage } = useIntl();\n\n const isMonthly = cycleToShow === 'monthly';\n const frequency = isMonthly ? 'month' : 'year';\n const cycleLabel = isMonthly\n ? formatMessage(messages.monthlySubscription)\n : formatMessage(messages.annualSubscription);\n\n return (\n <div className=\"enrollment-summary\">\n <Card className=\"enrollment-summary\">\n <Card.Section>\n <p className=\"font-weight-bold mb-3\">\n {formatMessage(messages.purchaseSummary)}\n </p>\n\n <div className=\"d-flex align-items-start mb-3\">\n <EdxUnlimitedLogo />\n <div>\n <p className=\"font-weight-bold mb-1\" style={{ fontSize: '0.9rem' }}>\n {productTitle}\n </p>\n <p className=\"text-muted mb-0\" style={{ fontSize: '0.82rem' }}>\n {productBlurb}\n </p>\n </div>\n </div>\n\n <div className=\"d-flex justify-content-between align-items-center mb-1\">\n <span className=\"small text-muted\">{cycleLabel}</span>\n {amount && (\n <span className=\"small font-weight-bold\">\n {formatMessage(messages.priceDisplay, {\n amount,\n frequency,\n currency,\n })}\n </span>\n )}\n </div>\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"d-flex justify-content-between align-items-center\">\n <span className=\"font-weight-bold\">\n {formatMessage(messages.todaysTotal)}\n </span>\n <span className=\"font-weight-bold text-success\">\n {formatMessage(messages.todaysTotalValue)}\n </span>\n </Card.Section>\n </Card>\n\n {course && (\n <CourseEnrollmentCard\n course={course}\n variant=\"success\"\n onStartCourse={onStartCourse}\n />\n )}\n </div>\n );\n};\n\nexport default EnrollmentSummary;\n"]}
@@ -0,0 +1,33 @@
1
+ declare const messages: {
2
+ purchaseSummary: {
3
+ id: string;
4
+ defaultMessage: string;
5
+ description: string;
6
+ };
7
+ monthlySubscription: {
8
+ id: string;
9
+ defaultMessage: string;
10
+ description: string;
11
+ };
12
+ annualSubscription: {
13
+ id: string;
14
+ defaultMessage: string;
15
+ description: string;
16
+ };
17
+ priceDisplay: {
18
+ id: string;
19
+ defaultMessage: string;
20
+ description: string;
21
+ };
22
+ todaysTotal: {
23
+ id: string;
24
+ defaultMessage: string;
25
+ description: string;
26
+ };
27
+ todaysTotalValue: {
28
+ id: string;
29
+ defaultMessage: string;
30
+ description: string;
31
+ };
32
+ };
33
+ export default messages;
@@ -0,0 +1,35 @@
1
+ import { defineMessages } from '@openedx/frontend-base';
2
+ const messages = defineMessages({
3
+ purchaseSummary: {
4
+ id: 'checkout.enrollmentSummary.purchaseSummary',
5
+ defaultMessage: 'Purchase summary',
6
+ description: 'Heading for the purchase summary card on the success screen',
7
+ },
8
+ monthlySubscription: {
9
+ id: 'checkout.enrollmentSummary.monthlySubscription',
10
+ defaultMessage: 'Monthly subscription',
11
+ description: 'Label for monthly billing cycle in purchase summary',
12
+ },
13
+ annualSubscription: {
14
+ id: 'checkout.enrollmentSummary.annualSubscription',
15
+ defaultMessage: 'Annual subscription',
16
+ description: 'Label for annual billing cycle in purchase summary',
17
+ },
18
+ priceDisplay: {
19
+ id: 'checkout.enrollmentSummary.priceDisplay',
20
+ defaultMessage: '${amount}/{frequency} USD',
21
+ description: 'Price display in the purchase summary',
22
+ },
23
+ todaysTotal: {
24
+ id: 'checkout.enrollmentSummary.todaysTotal',
25
+ defaultMessage: "Today's total",
26
+ description: "Label for today's total row",
27
+ },
28
+ todaysTotalValue: {
29
+ id: 'checkout.enrollmentSummary.todaysTotalValue',
30
+ defaultMessage: '$0',
31
+ description: "Today's total value during trial period",
32
+ },
33
+ });
34
+ export default messages;
35
+ //# sourceMappingURL=Messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/EnrollmentSummary/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,eAAe,EAAE;QACf,EAAE,EAAE,4CAA4C;QAChD,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,6DAA6D;KAC3E;IACD,mBAAmB,EAAE;QACnB,EAAE,EAAE,gDAAgD;QACpD,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,qDAAqD;KACnE;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,+CAA+C;QACnD,cAAc,EAAE,qBAAqB;QACrC,WAAW,EAAE,oDAAoD;KAClE;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,2BAA2B;QAC3C,WAAW,EAAE,uCAAuC;KACrD;IACD,WAAW,EAAE;QACX,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,eAAe;QAC/B,WAAW,EAAE,6BAA6B;KAC3C;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,6CAA6C;QACjD,cAAc,EAAE,IAAI;QACpB,WAAW,EAAE,yCAAyC;KACvD;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n purchaseSummary: {\n id: 'checkout.enrollmentSummary.purchaseSummary',\n defaultMessage: 'Purchase summary',\n description: 'Heading for the purchase summary card on the success screen',\n },\n monthlySubscription: {\n id: 'checkout.enrollmentSummary.monthlySubscription',\n defaultMessage: 'Monthly subscription',\n description: 'Label for monthly billing cycle in purchase summary',\n },\n annualSubscription: {\n id: 'checkout.enrollmentSummary.annualSubscription',\n defaultMessage: 'Annual subscription',\n description: 'Label for annual billing cycle in purchase summary',\n },\n priceDisplay: {\n id: 'checkout.enrollmentSummary.priceDisplay',\n defaultMessage: '${amount}/{frequency} USD',\n description: 'Price display in the purchase summary',\n },\n todaysTotal: {\n id: 'checkout.enrollmentSummary.todaysTotal',\n defaultMessage: \"Today's total\",\n description: \"Label for today's total row\",\n },\n todaysTotalValue: {\n id: 'checkout.enrollmentSummary.todaysTotalValue',\n defaultMessage: '$0',\n description: \"Today's total value during trial period\",\n },\n});\n\nexport default messages;\n"]}
@@ -0,0 +1,9 @@
1
+ import type { Ref } from 'react';
2
+ export interface PaymentFieldsProps {
3
+ numberMountRef: Ref<HTMLDivElement>;
4
+ expiryMountRef: Ref<HTMLDivElement>;
5
+ cvcMountRef: Ref<HTMLDivElement>;
6
+ cardError: string;
7
+ }
8
+ declare const PaymentFields: ({ numberMountRef, expiryMountRef, cvcMountRef, cardError, }: PaymentFieldsProps) => import("react/jsx-runtime").JSX.Element;
9
+ export default PaymentFields;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Form } from '@openedx/paragon';
3
+ import { STRIPE_FIELD_STYLE } from '../../constants';
4
+ import { CardBrands } from '../BrandMarks/BrandMarks';
5
+ const PaymentFields = ({ numberMountRef, expiryMountRef, cvcMountRef, cardError, }) => (_jsxs("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: "text-gray-900 m-0 x-small", children: "Card number" }), _jsxs("div", { className: "form-control", style: STRIPE_FIELD_STYLE, children: [_jsx("div", { ref: numberMountRef, className: "flex-grow-1" }), _jsx(CardBrands, {})] })] }), _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: "text-gray-900 m-0 x-small", children: "Expiration" }), _jsx("div", { className: "form-control", style: STRIPE_FIELD_STYLE, children: _jsx("div", { ref: expiryMountRef, className: "flex-grow-1" }) })] }), _jsxs(Form.Group, { controlId: "cardCvc", className: "card__cvc mb-0 w-50", children: [_jsx(Form.Label, { className: "text-gray-900 m-0 x-small", children: "CVC" }), _jsx("div", { className: "form-control", style: STRIPE_FIELD_STYLE, children: _jsx("div", { ref: cvcMountRef, className: "flex-grow-1" }) })] })] })] }), cardError && _jsx("p", { className: "text-danger small mb-2", children: cardError })] }));
6
+ export default PaymentFields;
7
+ //# sourceMappingURL=PaymentFields.js.map
@@ -0,0 +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;AAStD,MAAM,aAAa,GAAG,CAAC,EACrB,cAAc,EACd,cAAc,EACd,WAAW,EACX,SAAS,GACU,EAAE,EAAE,CAAC,CACxB,eAAK,SAAS,EAAC,gBAAgB,aAE7B,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,2BAA2B,4BAEpC,EACb,eAAK,SAAS,EAAC,cAAc,EAAC,KAAK,EAAE,kBAAkB,aACrD,cAAK,GAAG,EAAE,cAAc,EAAE,SAAS,EAAC,aAAa,GAAG,EACpD,KAAC,UAAU,KAAG,IACV,IACK,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,2BAA2B,2BAEpC,EACb,cAAK,SAAS,EAAC,cAAc,EAAC,KAAK,EAAE,kBAAkB,YACrD,cAAK,GAAG,EAAE,cAAc,EAAE,SAAS,EAAC,aAAa,GAAG,GAChD,IACK,EAGb,MAAC,IAAI,CAAC,KAAK,IAAC,SAAS,EAAC,SAAS,EAAC,SAAS,EAAC,qBAAqB,aAC7D,KAAC,IAAI,CAAC,KAAK,IAAC,SAAS,EAAC,2BAA2B,oBAAiB,EAClE,cAAK,SAAS,EAAC,cAAc,EAAC,KAAK,EAAE,kBAAkB,YACrD,cAAK,GAAG,EAAE,WAAW,EAAE,SAAS,EAAC,aAAa,GAAG,GAC7C,IACK,IACT,IACF,EAEL,SAAS,IAAI,YAAG,SAAS,EAAC,wBAAwB,YAAE,SAAS,GAAK,IAC/D,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';\n\nexport interface PaymentFieldsProps {\n numberMountRef: Ref<HTMLDivElement>,\n expiryMountRef: Ref<HTMLDivElement>,\n cvcMountRef: Ref<HTMLDivElement>,\n cardError: string,\n}\n\nconst PaymentFields = ({\n numberMountRef,\n expiryMountRef,\n cvcMountRef,\n cardError,\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=\"text-gray-900 m-0 x-small\">\n Card number\n </Form.Label>\n <div className=\"form-control\" style={STRIPE_FIELD_STYLE}>\n <div ref={numberMountRef} className=\"flex-grow-1\" />\n <CardBrands />\n </div>\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=\"text-gray-900 m-0 x-small\">\n Expiration\n </Form.Label>\n <div className=\"form-control\" style={STRIPE_FIELD_STYLE}>\n <div ref={expiryMountRef} className=\"flex-grow-1\" />\n </div>\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=\"text-gray-900 m-0 x-small\">CVC</Form.Label>\n <div className=\"form-control\" style={STRIPE_FIELD_STYLE}>\n <div ref={cvcMountRef} className=\"flex-grow-1\" />\n </div>\n </Form.Group>\n </div>\n </div>\n\n {cardError && <p className=\"text-danger small mb-2\">{cardError}</p>}\n </div>\n);\n\nexport default PaymentFields;\n"]}
@@ -0,0 +1,88 @@
1
+ declare const messages: {
2
+ selectPaymentMethod: {
3
+ id: string;
4
+ defaultMessage: string;
5
+ description: string;
6
+ };
7
+ needAssistance: {
8
+ id: string;
9
+ defaultMessage: string;
10
+ description: string;
11
+ };
12
+ needAssistanceBody: {
13
+ id: string;
14
+ defaultMessage: string;
15
+ description: string;
16
+ };
17
+ learnerHelpCenter: {
18
+ id: string;
19
+ defaultMessage: string;
20
+ description: string;
21
+ };
22
+ card: {
23
+ id: string;
24
+ defaultMessage: string;
25
+ description: string;
26
+ };
27
+ googlePay: {
28
+ id: string;
29
+ defaultMessage: string;
30
+ description: string;
31
+ };
32
+ paypal: {
33
+ id: string;
34
+ defaultMessage: string;
35
+ description: string;
36
+ };
37
+ termsNotice: {
38
+ id: string;
39
+ defaultMessage: string;
40
+ description: string;
41
+ };
42
+ termsOfUse: {
43
+ id: string;
44
+ defaultMessage: string;
45
+ description: string;
46
+ };
47
+ refundPolicy: {
48
+ id: string;
49
+ defaultMessage: string;
50
+ description: string;
51
+ };
52
+ recurringConfirm: {
53
+ id: string;
54
+ defaultMessage: string;
55
+ description: string;
56
+ };
57
+ ukCaResidentConfirm: {
58
+ id: string;
59
+ defaultMessage: string;
60
+ description: string;
61
+ };
62
+ confirmSubscription: {
63
+ id: string;
64
+ defaultMessage: string;
65
+ description: string;
66
+ };
67
+ processing: {
68
+ id: string;
69
+ defaultMessage: string;
70
+ description: string;
71
+ };
72
+ loadingPayment: {
73
+ id: string;
74
+ defaultMessage: string;
75
+ description: string;
76
+ };
77
+ country: {
78
+ id: string;
79
+ defaultMessage: string;
80
+ description: string;
81
+ };
82
+ spinnerScreenReaderText: {
83
+ id: string;
84
+ defaultMessage: string;
85
+ description: string;
86
+ };
87
+ };
88
+ export default messages;
@@ -0,0 +1,95 @@
1
+ import { defineMessages } from '@openedx/frontend-base';
2
+ const messages = defineMessages({
3
+ // Section headings
4
+ selectPaymentMethod: {
5
+ id: 'checkout.paymentForm.selectPaymentMethod',
6
+ defaultMessage: 'Select Payment Method',
7
+ description: 'Heading for the payment method selection section',
8
+ },
9
+ needAssistance: {
10
+ id: 'checkout.paymentForm.needAssistance',
11
+ defaultMessage: 'Need assistance?',
12
+ description: 'Help text heading',
13
+ },
14
+ needAssistanceBody: {
15
+ id: 'checkout.paymentForm.needAssistanceBody',
16
+ defaultMessage: 'Visit the {link} to review purchase and refund policies.',
17
+ description: 'Help text with learner help center link',
18
+ },
19
+ learnerHelpCenter: {
20
+ id: 'checkout.paymentForm.learnerHelpCenter',
21
+ defaultMessage: 'Learner Help Center',
22
+ description: 'Learner help center link text',
23
+ },
24
+ // Payment method options
25
+ card: {
26
+ id: 'checkout.paymentForm.card',
27
+ defaultMessage: 'Card',
28
+ description: 'Credit/debit card payment method label',
29
+ },
30
+ googlePay: {
31
+ id: 'checkout.paymentForm.googlePay',
32
+ defaultMessage: 'Google Pay',
33
+ description: 'Google Pay payment method label',
34
+ },
35
+ paypal: {
36
+ id: 'checkout.paymentForm.paypal',
37
+ defaultMessage: 'PayPal',
38
+ description: 'PayPal payment method label',
39
+ },
40
+ // Terms
41
+ termsNotice: {
42
+ id: 'checkout.paymentForm.termsNotice',
43
+ defaultMessage: 'Your subscription will start today with a 7-day free trial. Your subscription automatically renews on {renewalDate} for {price}/{frequency} USD (inclusive of applicable taxes). Cancel your subscription anytime from your dashboard. By clicking "Confirm subscription" you agree to our {termsLink}, and {refundLink}.',
44
+ description: 'Subscription terms notice shown before the confirm button',
45
+ },
46
+ termsOfUse: {
47
+ id: 'checkout.paymentForm.termsOfUse',
48
+ defaultMessage: 'Terms of Use',
49
+ description: 'Terms of Use link text',
50
+ },
51
+ refundPolicy: {
52
+ id: 'checkout.paymentForm.refundPolicy',
53
+ defaultMessage: 'Refund Policy',
54
+ description: 'Refund Policy link text',
55
+ },
56
+ // Checkboxes
57
+ recurringConfirm: {
58
+ id: 'checkout.paymentForm.recurringConfirm',
59
+ defaultMessage: 'I agree to enroll in a recurring {cycle} subscription for {price}/{frequency} USD.',
60
+ description: 'First checkbox confirming recurring charge',
61
+ },
62
+ ukCaResidentConfirm: {
63
+ id: 'checkout.paymentForm.ukCaResidentConfirm',
64
+ defaultMessage: 'Second checkbox only for UK and CA residents',
65
+ description: 'Second checkbox for UK and CA residents',
66
+ },
67
+ // Submit button
68
+ confirmSubscription: {
69
+ id: 'checkout.paymentForm.confirmSubscription',
70
+ defaultMessage: 'Confirm subscription',
71
+ description: 'Submit button label',
72
+ },
73
+ processing: {
74
+ id: 'checkout.paymentForm.processing',
75
+ defaultMessage: 'Processing…',
76
+ description: 'Submit button label while processing',
77
+ },
78
+ loadingPayment: {
79
+ id: 'checkout.paymentForm.loadingPayment',
80
+ defaultMessage: 'Loading payment…',
81
+ description: 'Submit button label while Stripe loads',
82
+ },
83
+ country: {
84
+ id: 'checkout.paymentForm.country',
85
+ defaultMessage: 'Country',
86
+ description: 'Country selector label',
87
+ },
88
+ spinnerScreenReaderText: {
89
+ id: 'checkout.paymentForm.spinnerScreenReaderText',
90
+ defaultMessage: 'Working',
91
+ description: 'Screen reader text for the loading spinner',
92
+ },
93
+ });
94
+ export default messages;
95
+ //# sourceMappingURL=Messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/PaymentForm/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,mBAAmB;IACnB,mBAAmB,EAAE;QACnB,EAAE,EAAE,0CAA0C;QAC9C,cAAc,EAAE,uBAAuB;QACvC,WAAW,EAAE,kDAAkD;KAChE;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,mBAAmB;KACjC;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,0DAA0D;QAC1E,WAAW,EAAE,yCAAyC;KACvD;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,qBAAqB;QACrC,WAAW,EAAE,+BAA+B;KAC7C;IAED,yBAAyB;IACzB,IAAI,EAAE;QACJ,EAAE,EAAE,2BAA2B;QAC/B,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,wCAAwC;KACtD;IACD,SAAS,EAAE;QACT,EAAE,EAAE,gCAAgC;QACpC,cAAc,EAAE,YAAY;QAC5B,WAAW,EAAE,iCAAiC;KAC/C;IACD,MAAM,EAAE;QACN,EAAE,EAAE,6BAA6B;QACjC,cAAc,EAAE,QAAQ;QACxB,WAAW,EAAE,6BAA6B;KAC3C;IAED,QAAQ;IACR,WAAW,EAAE;QACX,EAAE,EAAE,kCAAkC;QACtC,cAAc,EAAE,2TAA2T;QAC3U,WAAW,EAAE,2DAA2D;KACzE;IACD,UAAU,EAAE;QACV,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,wBAAwB;KACtC;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,eAAe;QAC/B,WAAW,EAAE,yBAAyB;KACvC;IAED,aAAa;IACb,gBAAgB,EAAE;QAChB,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,oFAAoF;QACpG,WAAW,EAAE,4CAA4C;KAC1D;IACD,mBAAmB,EAAE;QACnB,EAAE,EAAE,0CAA0C;QAC9C,cAAc,EAAE,8CAA8C;QAC9D,WAAW,EAAE,yCAAyC;KACvD;IAED,gBAAgB;IAChB,mBAAmB,EAAE;QACnB,EAAE,EAAE,0CAA0C;QAC9C,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,qBAAqB;KACnC;IACD,UAAU,EAAE;QACV,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,aAAa;QAC7B,WAAW,EAAE,sCAAsC;KACpD;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,wCAAwC;KACtD;IACD,OAAO,EAAE;QACP,EAAE,EAAE,8BAA8B;QAClC,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,wBAAwB;KACtC;IACD,uBAAuB,EAAE;QACvB,EAAE,EAAE,8CAA8C;QAClD,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,4CAA4C;KAC1D;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n // Section headings\n selectPaymentMethod: {\n id: 'checkout.paymentForm.selectPaymentMethod',\n defaultMessage: 'Select Payment Method',\n description: 'Heading for the payment method selection section',\n },\n needAssistance: {\n id: 'checkout.paymentForm.needAssistance',\n defaultMessage: 'Need assistance?',\n description: 'Help text heading',\n },\n needAssistanceBody: {\n id: 'checkout.paymentForm.needAssistanceBody',\n defaultMessage: 'Visit the {link} to review purchase and refund policies.',\n description: 'Help text with learner help center link',\n },\n learnerHelpCenter: {\n id: 'checkout.paymentForm.learnerHelpCenter',\n defaultMessage: 'Learner Help Center',\n description: 'Learner help center link text',\n },\n\n // Payment method options\n card: {\n id: 'checkout.paymentForm.card',\n defaultMessage: 'Card',\n description: 'Credit/debit card payment method label',\n },\n googlePay: {\n id: 'checkout.paymentForm.googlePay',\n defaultMessage: 'Google Pay',\n description: 'Google Pay payment method label',\n },\n paypal: {\n id: 'checkout.paymentForm.paypal',\n defaultMessage: 'PayPal',\n description: 'PayPal payment method label',\n },\n\n // Terms\n termsNotice: {\n id: 'checkout.paymentForm.termsNotice',\n defaultMessage: 'Your subscription will start today with a 7-day free trial. Your subscription automatically renews on {renewalDate} for {price}/{frequency} USD (inclusive of applicable taxes). Cancel your subscription anytime from your dashboard. By clicking \"Confirm subscription\" you agree to our {termsLink}, and {refundLink}.',\n description: 'Subscription terms notice shown before the confirm button',\n },\n termsOfUse: {\n id: 'checkout.paymentForm.termsOfUse',\n defaultMessage: 'Terms of Use',\n description: 'Terms of Use link text',\n },\n refundPolicy: {\n id: 'checkout.paymentForm.refundPolicy',\n defaultMessage: 'Refund Policy',\n description: 'Refund Policy link text',\n },\n\n // Checkboxes\n recurringConfirm: {\n id: 'checkout.paymentForm.recurringConfirm',\n defaultMessage: 'I agree to enroll in a recurring {cycle} subscription for {price}/{frequency} USD.',\n description: 'First checkbox confirming recurring charge',\n },\n ukCaResidentConfirm: {\n id: 'checkout.paymentForm.ukCaResidentConfirm',\n defaultMessage: 'Second checkbox only for UK and CA residents',\n description: 'Second checkbox for UK and CA residents',\n },\n\n // Submit button\n confirmSubscription: {\n id: 'checkout.paymentForm.confirmSubscription',\n defaultMessage: 'Confirm subscription',\n description: 'Submit button label',\n },\n processing: {\n id: 'checkout.paymentForm.processing',\n defaultMessage: 'Processing…',\n description: 'Submit button label while processing',\n },\n loadingPayment: {\n id: 'checkout.paymentForm.loadingPayment',\n defaultMessage: 'Loading payment…',\n description: 'Submit button label while Stripe loads',\n },\n country: {\n id: 'checkout.paymentForm.country',\n defaultMessage: 'Country',\n description: 'Country selector label',\n },\n spinnerScreenReaderText: {\n id: 'checkout.paymentForm.spinnerScreenReaderText',\n defaultMessage: 'Working',\n description: 'Screen reader text for the loading spinner',\n },\n});\n\nexport default messages;\n"]}
@@ -0,0 +1,21 @@
1
+ import type { Ref } from 'react';
2
+ import type { BillingInfo, CheckoutConsent } from '../../types';
3
+ import type { PlanPrice } from '../../utils/billingDetails';
4
+ export interface PaymentFormProps {
5
+ billing: BillingInfo;
6
+ setField: (field: keyof BillingInfo, value: string) => void;
7
+ numberMountRef: Ref<HTMLDivElement>;
8
+ expiryMountRef: Ref<HTMLDivElement>;
9
+ cvcMountRef: Ref<HTMLDivElement>;
10
+ cardError: string;
11
+ stripeReady: boolean;
12
+ sessionReady: boolean;
13
+ submitting: boolean;
14
+ onPlaceOrder: (consent: CheckoutConsent) => void;
15
+ priceToShow: PlanPrice | null;
16
+ cycleToShow: string;
17
+ trialEnd?: string;
18
+ userCountry?: string | null;
19
+ }
20
+ declare const PaymentForm: ({ billing, setField, numberMountRef, expiryMountRef, cvcMountRef, cardError, stripeReady, sessionReady, submitting, onPlaceOrder, priceToShow, cycleToShow, trialEnd, userCountry, }: PaymentFormProps) => import("react/jsx-runtime").JSX.Element;
21
+ export default PaymentForm;
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Button, Form, Spinner } from '@openedx/paragon';
4
+ import { useIntl } from '@openedx/frontend-base';
5
+ import PaymentFields from '../PaymentFields/PaymentFields';
6
+ import { COUNTRIES_REQUIRING_EXTRA_CONFIRM, COUNTRIES } from '../../constants';
7
+ import messages from './Messages';
8
+ const PaymentForm = ({ billing, setField, numberMountRef, expiryMountRef, cvcMountRef, cardError, stripeReady, sessionReady, submitting, onPlaceOrder, priceToShow, cycleToShow, trialEnd, userCountry, }) => {
9
+ var _a;
10
+ const { formatMessage } = useIntl();
11
+ const [paymentMethod, setPaymentMethod] = useState('card');
12
+ const [confirmed, setConfirmed] = useState(false);
13
+ const [ukCaConfirmed, setUkCaConfirmed] = useState(false);
14
+ const showExtraConfirm = !!userCountry && COUNTRIES_REQUIRING_EXTRA_CONFIRM.includes(userCountry);
15
+ const busy = submitting || !stripeReady || !sessionReady;
16
+ let buttonLabel = formatMessage(messages.confirmSubscription);
17
+ if (submitting)
18
+ buttonLabel = formatMessage(messages.processing);
19
+ else if (!sessionReady)
20
+ buttonLabel = formatMessage(messages.loadingPayment);
21
+ else if (!stripeReady)
22
+ buttonLabel = formatMessage(messages.loadingPayment);
23
+ const amount = (_a = priceToShow === null || priceToShow === void 0 ? void 0 : priceToShow.amount) !== null && _a !== void 0 ? _a : '';
24
+ const frequency = cycleToShow === 'annual' ? 'year' : 'month';
25
+ const buildConsent = () => {
26
+ const termsText = formatMessage(messages.termsNotice, {
27
+ renewalDate: trialEnd !== null && trialEnd !== void 0 ? trialEnd : '{date}',
28
+ price: amount,
29
+ frequency,
30
+ termsLink: formatMessage(messages.termsOfUse),
31
+ refundLink: formatMessage(messages.refundPolicy),
32
+ });
33
+ const recurringText = formatMessage(messages.recurringConfirm, {
34
+ cycle: cycleToShow,
35
+ price: amount,
36
+ frequency,
37
+ });
38
+ const consentParts = [termsText, recurringText];
39
+ if (showExtraConfirm) {
40
+ consentParts.push(formatMessage(messages.ukCaResidentConfirm));
41
+ }
42
+ return {
43
+ tConsentRecurring: confirmed,
44
+ tConsentUkCa: showExtraConfirm ? ukCaConfirmed : null,
45
+ tConsentAt: new Date().toISOString(),
46
+ tConsentText: consentParts.join('\n\n'),
47
+ };
48
+ };
49
+ return (_jsxs("div", { className: "payment-form bg-white", children: [_jsx("h2", { className: "payment-form__heading font-weight-bold mb-3 text-primary-500", children: formatMessage(messages.selectPaymentMethod) }), _jsxs("p", { className: "payment-form__help small mb-4", children: [_jsx("strong", { className: "d-block text-primary-500", children: formatMessage(messages.needAssistance) }), ' ', _jsx("span", { className: "text-gray-700", children: formatMessage(messages.needAssistanceBody, {
50
+ link: (_jsx("a", { href: "https://support.edx.org", target: "_blank", rel: "noreferrer", className: "text-gray-700", children: formatMessage(messages.learnerHelpCenter) })),
51
+ }) })] }), _jsxs("div", { className: "payment-form__methods mb-4", children: [_jsxs("div", { className: `payment-form__method${paymentMethod === 'card' ? ' payment-form__method--selected' : ''}`, children: [_jsxs("label", { htmlFor: "paymentMethod-card", className: "payment-form__method-header", children: [_jsx("input", { id: "paymentMethod-card", type: "radio", name: "paymentMethod", value: "card", checked: paymentMethod === 'card', onChange: () => setPaymentMethod('card'), className: "payment-form__radio" }), _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M0 4C0 3.46957 0.210714 2.96086 0.585786 2.58579C0.960859 2.21071 1.46957 2 2 2H14C14.5304 2 15.0391 2.21071 15.4142 2.58579C15.7893 2.96086 16 3.46957 16 4H0ZM0 6V12C0 12.5304 0.210714 13.0391 0.585786 13.4142C0.960859 13.7893 1.46957 14 2 14H14C14.5304 14 15.0391 13.7893 15.4142 13.4142C15.7893 13.0391 16 12.5304 16 12V6H0ZM3 11C3 10.7348 3.10536 10.4804 3.29289 10.2929C3.48043 10.1054 3.73478 10 4 10H5C5.26522 10 5.51957 10.1054 5.70711 10.2929C5.89464 10.4804 6 10.7348 6 11C6 11.2652 5.89464 11.5196 5.70711 11.7071C5.51957 11.8946 5.26522 12 5 12H4C3.73478 12 3.48043 11.8946 3.29289 11.7071C3.10536 11.5196 3 11.2652 3 11Z", fill: "#0270DE" }) }), _jsx("span", { className: "payment-form__method-name text-info-500", children: formatMessage(messages.card) })] }), paymentMethod === 'card' && (_jsxs("div", { className: "payment-form__card-panel", children: [_jsx(PaymentFields, { numberMountRef: numberMountRef, expiryMountRef: expiryMountRef, cvcMountRef: cvcMountRef, cardError: cardError }), _jsxs(Form.Group, { controlId: "country", className: "mt-3 mb-0 payment__field--country", children: [_jsx(Form.Label, { className: "small m-0 x-small", children: formatMessage(messages.country) }), _jsx(Form.Control, { as: "select", value: billing.country, onChange: (e) => setField('country', e.target.value), children: COUNTRIES.map((country) => {
52
+ return (_jsx("option", { value: country[0], children: country[1] }, country[0]));
53
+ }) })] })] }))] }), _jsx("div", { className: `my-3 payment-form__method${paymentMethod === 'googlepay' ? ' payment-form__method--selected' : ''}`, children: _jsxs("label", { htmlFor: "paymentMethod-googlepay", className: "payment-form__method-header", children: [_jsx("input", { id: "paymentMethod-googlepay", type: "radio", name: "paymentMethod", value: "googlepay", checked: paymentMethod === 'googlepay', onChange: () => setPaymentMethod('googlepay'), className: "payment-form__radio" }), _jsx("span", { className: "payment-form__method-icon payment-form__method-icon--gpay", "aria-hidden": "true", children: "G" }), _jsx("span", { className: "payment-form__method-name", children: formatMessage(messages.googlePay) })] }) }), _jsx("div", { className: `payment-form__method${paymentMethod === 'paypal' ? ' payment-form__method--selected' : ''}`, children: _jsxs("label", { htmlFor: "paymentMethod-paypal", className: "payment-form__method-header", children: [_jsx("input", { id: "paymentMethod-paypal", type: "radio", name: "paymentMethod", value: "paypal", checked: paymentMethod === 'paypal', onChange: () => setPaymentMethod('paypal'), className: "payment-form__radio" }), _jsx("span", { className: "payment-form__method-icon payment-form__method-icon--paypal", "aria-hidden": "true", children: "P" }), _jsx("span", { className: "payment-form__method-name", children: formatMessage(messages.paypal) })] }) })] }), _jsx("p", { className: "payment-form__terms text-gray-700 mb-3", children: formatMessage(messages.termsNotice, {
54
+ renewalDate: trialEnd !== null && trialEnd !== void 0 ? trialEnd : '{date}',
55
+ price: amount,
56
+ frequency,
57
+ termsLink: (_jsx("a", { className: "text-gray-700", href: "/terms", target: "_blank", rel: "noreferrer", children: formatMessage(messages.termsOfUse) })),
58
+ refundLink: (_jsx("a", { className: "text-gray-700", href: "/refund-policy", target: "_blank", rel: "noreferrer", children: formatMessage(messages.refundPolicy) })),
59
+ }) }), _jsx(Form.Group, { controlId: "recurringConfirm", className: "mb-2", children: _jsx(Form.Checkbox, { id: "recurringConfirm", checked: confirmed, onChange: (e) => setConfirmed(e.target.checked), children: _jsx("span", { className: "text-gray-700", children: formatMessage(messages.recurringConfirm, {
60
+ cycle: cycleToShow,
61
+ price: amount,
62
+ frequency,
63
+ }) }) }) }), showExtraConfirm && (_jsx(Form.Group, { controlId: "ukCaConfirm", className: "mb-4", children: _jsx(Form.Checkbox, { id: "ukCaConfirm", checked: ukCaConfirmed, onChange: (e) => setUkCaConfirmed(e.target.checked), children: _jsx("span", { className: "text-gray-700", children: formatMessage(messages.ukCaResidentConfirm) }) }) })), _jsxs(Button, { variant: "brand", className: "payment-form__submit", onClick: () => onPlaceOrder(buildConsent()), disabled: !stripeReady
64
+ || submitting
65
+ || !confirmed
66
+ || (showExtraConfirm && !ukCaConfirmed), children: [busy && (_jsx(Spinner, { animation: "border", size: "sm", className: "mr-2", screenReaderText: formatMessage(messages.spinnerScreenReaderText) })), buttonLabel] })] }));
67
+ };
68
+ export default PaymentForm;
69
+ //# sourceMappingURL=PaymentForm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PaymentForm.js","sourceRoot":"","sources":["../../../src/components/PaymentForm/PaymentForm.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useState } from 'react';\nimport type { Ref } from 'react';\nimport { Button, Form, Spinner } from '@openedx/paragon';\nimport { useIntl } from '@openedx/frontend-base';\nimport PaymentFields from '../PaymentFields/PaymentFields';\nimport type { BillingInfo, CheckoutConsent } from '../../types';\nimport type { PlanPrice } from '../../utils/billingDetails';\nimport { COUNTRIES_REQUIRING_EXTRA_CONFIRM, COUNTRIES } from '../../constants';\nimport messages from './Messages';\n\ntype PaymentMethod = 'card' | 'googlepay' | 'paypal';\n\nexport interface PaymentFormProps {\n billing: BillingInfo,\n setField: (field: keyof BillingInfo, value: string) => void,\n numberMountRef: Ref<HTMLDivElement>,\n expiryMountRef: Ref<HTMLDivElement>,\n cvcMountRef: Ref<HTMLDivElement>,\n cardError: string,\n stripeReady: boolean,\n sessionReady: boolean,\n submitting: boolean,\n onPlaceOrder: (consent: CheckoutConsent) => void,\n priceToShow: PlanPrice | null,\n cycleToShow: string,\n trialEnd?: string,\n userCountry?: string | null,\n}\n\nconst PaymentForm = ({\n billing,\n setField,\n numberMountRef,\n expiryMountRef,\n cvcMountRef,\n cardError,\n stripeReady,\n sessionReady,\n submitting,\n onPlaceOrder,\n priceToShow,\n cycleToShow,\n trialEnd,\n userCountry,\n}: PaymentFormProps) => {\n const { formatMessage } = useIntl();\n const [paymentMethod, setPaymentMethod] = useState<PaymentMethod>('card');\n const [confirmed, setConfirmed] = useState(false);\n const [ukCaConfirmed, setUkCaConfirmed] = useState(false);\n\n const showExtraConfirm\n = !!userCountry && COUNTRIES_REQUIRING_EXTRA_CONFIRM.includes(userCountry);\n\n const busy = submitting || !stripeReady || !sessionReady;\n\n let buttonLabel = formatMessage(messages.confirmSubscription);\n if (submitting) buttonLabel = formatMessage(messages.processing);\n else if (!sessionReady) buttonLabel = formatMessage(messages.loadingPayment);\n else if (!stripeReady) buttonLabel = formatMessage(messages.loadingPayment);\n\n const amount = priceToShow?.amount ?? '';\n const frequency = cycleToShow === 'annual' ? 'year' : 'month';\n\n const buildConsent = (): CheckoutConsent => {\n const termsText = formatMessage(messages.termsNotice, {\n renewalDate: trialEnd ?? '{date}',\n price: amount,\n frequency,\n termsLink: formatMessage(messages.termsOfUse),\n refundLink: formatMessage(messages.refundPolicy),\n });\n const recurringText = formatMessage(messages.recurringConfirm, {\n cycle: cycleToShow,\n price: amount,\n frequency,\n });\n const consentParts = [termsText, recurringText];\n if (showExtraConfirm) {\n consentParts.push(formatMessage(messages.ukCaResidentConfirm));\n }\n return {\n tConsentRecurring: confirmed,\n tConsentUkCa: showExtraConfirm ? ukCaConfirmed : null,\n tConsentAt: new Date().toISOString(),\n tConsentText: consentParts.join('\\n\\n'),\n };\n };\n\n return (\n <div className=\"payment-form bg-white\">\n <h2 className=\"payment-form__heading font-weight-bold mb-3 text-primary-500\">\n {formatMessage(messages.selectPaymentMethod)}\n </h2>\n\n <p className=\"payment-form__help small mb-4\">\n <strong className=\"d-block text-primary-500\">\n {formatMessage(messages.needAssistance)}\n </strong>{' '}\n <span className=\"text-gray-700\">\n {formatMessage(messages.needAssistanceBody, {\n link: (\n <a\n href=\"https://support.edx.org\"\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-gray-700\"\n >\n {formatMessage(messages.learnerHelpCenter)}\n </a>\n ),\n })}\n </span>\n </p>\n\n {/* ── Payment method accordion ── */}\n <div className=\"payment-form__methods mb-4\">\n {/* ── Card ── */}\n <div\n className={`payment-form__method${paymentMethod === 'card' ? ' payment-form__method--selected' : ''}`}\n >\n {/* Header row — always visible */}\n <label\n htmlFor=\"paymentMethod-card\"\n className=\"payment-form__method-header\"\n >\n <input\n id=\"paymentMethod-card\"\n type=\"radio\"\n name=\"paymentMethod\"\n value=\"card\"\n checked={paymentMethod === 'card'}\n onChange={() => setPaymentMethod('card')}\n className=\"payment-form__radio\"\n />\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M0 4C0 3.46957 0.210714 2.96086 0.585786 2.58579C0.960859 2.21071 1.46957 2 2 2H14C14.5304 2 15.0391 2.21071 15.4142 2.58579C15.7893 2.96086 16 3.46957 16 4H0ZM0 6V12C0 12.5304 0.210714 13.0391 0.585786 13.4142C0.960859 13.7893 1.46957 14 2 14H14C14.5304 14 15.0391 13.7893 15.4142 13.4142C15.7893 13.0391 16 12.5304 16 12V6H0ZM3 11C3 10.7348 3.10536 10.4804 3.29289 10.2929C3.48043 10.1054 3.73478 10 4 10H5C5.26522 10 5.51957 10.1054 5.70711 10.2929C5.89464 10.4804 6 10.7348 6 11C6 11.2652 5.89464 11.5196 5.70711 11.7071C5.51957 11.8946 5.26522 12 5 12H4C3.73478 12 3.48043 11.8946 3.29289 11.7071C3.10536 11.5196 3 11.2652 3 11Z\"\n fill=\"#0270DE\"\n />\n </svg>\n <span className=\"payment-form__method-name text-info-500\">\n {formatMessage(messages.card)}\n </span>\n </label>\n\n {/* Expanded panel — only when selected */}\n {paymentMethod === 'card' && (\n <div className=\"payment-form__card-panel\">\n <PaymentFields\n numberMountRef={numberMountRef}\n expiryMountRef={expiryMountRef}\n cvcMountRef={cvcMountRef}\n cardError={cardError}\n />\n <Form.Group\n controlId=\"country\"\n className=\"mt-3 mb-0 payment__field--country\"\n >\n <Form.Label className=\"small m-0 x-small\">\n {formatMessage(messages.country)}\n </Form.Label>\n <Form.Control\n as=\"select\"\n value={billing.country}\n onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>\n setField('country', e.target.value)}\n >\n {COUNTRIES.map((country) => {\n return (\n <option key={country[0]} value={country[0]}>\n {country[1]}\n </option>\n );\n })}\n </Form.Control>\n </Form.Group>\n </div>\n )}\n </div>\n\n {/* ── Google Pay ── */}\n <div\n className={`my-3 payment-form__method${paymentMethod === 'googlepay' ? ' payment-form__method--selected' : ''}`}\n >\n <label\n htmlFor=\"paymentMethod-googlepay\"\n className=\"payment-form__method-header\"\n >\n <input\n id=\"paymentMethod-googlepay\"\n type=\"radio\"\n name=\"paymentMethod\"\n value=\"googlepay\"\n checked={paymentMethod === 'googlepay'}\n onChange={() => setPaymentMethod('googlepay')}\n className=\"payment-form__radio\"\n />\n <span\n className=\"payment-form__method-icon payment-form__method-icon--gpay\"\n aria-hidden=\"true\"\n >\n G\n </span>\n <span className=\"payment-form__method-name\">\n {formatMessage(messages.googlePay)}\n </span>\n </label>\n </div>\n\n {/* ── PayPal ── */}\n <div\n className={`payment-form__method${paymentMethod === 'paypal' ? ' payment-form__method--selected' : ''}`}\n >\n <label\n htmlFor=\"paymentMethod-paypal\"\n className=\"payment-form__method-header\"\n >\n <input\n id=\"paymentMethod-paypal\"\n type=\"radio\"\n name=\"paymentMethod\"\n value=\"paypal\"\n checked={paymentMethod === 'paypal'}\n onChange={() => setPaymentMethod('paypal')}\n className=\"payment-form__radio\"\n />\n <span\n className=\"payment-form__method-icon payment-form__method-icon--paypal\"\n aria-hidden=\"true\"\n >\n P\n </span>\n <span className=\"payment-form__method-name\">\n {formatMessage(messages.paypal)}\n </span>\n </label>\n </div>\n </div>\n\n {/* ── Terms ── */}\n <p className=\"payment-form__terms text-gray-700 mb-3\">\n {formatMessage(messages.termsNotice, {\n renewalDate: trialEnd ?? '{date}',\n price: amount,\n frequency,\n termsLink: (\n <a\n className=\"text-gray-700\"\n href=\"/terms\"\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {formatMessage(messages.termsOfUse)}\n </a>\n ),\n refundLink: (\n <a\n className=\"text-gray-700\"\n href=\"/refund-policy\"\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {formatMessage(messages.refundPolicy)}\n </a>\n ),\n })}\n </p>\n\n {/* ── Checkboxes ── */}\n <Form.Group controlId=\"recurringConfirm\" className=\"mb-2\">\n <Form.Checkbox\n id=\"recurringConfirm\"\n checked={confirmed}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setConfirmed(e.target.checked)}\n >\n <span className=\"text-gray-700\">\n {formatMessage(messages.recurringConfirm, {\n cycle: cycleToShow,\n price: amount,\n frequency,\n })}\n </span>\n </Form.Checkbox>\n </Form.Group>\n\n {showExtraConfirm && (\n <Form.Group controlId=\"ukCaConfirm\" className=\"mb-4\">\n <Form.Checkbox\n id=\"ukCaConfirm\"\n checked={ukCaConfirmed}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n setUkCaConfirmed(e.target.checked)}\n >\n <span className=\"text-gray-700\">\n {formatMessage(messages.ukCaResidentConfirm)}\n </span>\n </Form.Checkbox>\n </Form.Group>\n )}\n\n {/* ── Submit ── */}\n <Button\n variant=\"brand\"\n className=\"payment-form__submit\"\n onClick={() => onPlaceOrder(buildConsent())}\n disabled={\n !stripeReady\n || submitting\n || !confirmed\n || (showExtraConfirm && !ukCaConfirmed)\n }\n >\n {busy && (\n <Spinner\n animation=\"border\"\n size=\"sm\"\n className=\"mr-2\"\n screenReaderText={formatMessage(messages.spinnerScreenReaderText)}\n />\n )}\n {buttonLabel}\n </Button>\n </div>\n );\n};\n\nexport default PaymentForm;\n"]}