@edx/frontend-app-subscription-checkout 1.11.0 → 1.13.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 (47) hide show
  1. package/dist/components/CartSummary/CartSummary.js +7 -2
  2. package/dist/components/CartSummary/CartSummary.js.map +1 -1
  3. package/dist/components/CartSummary/Messages.d.ts +5 -0
  4. package/dist/components/CartSummary/Messages.js +6 -1
  5. package/dist/components/CartSummary/Messages.js.map +1 -1
  6. package/dist/components/Checkout/Checkout.js +17 -8
  7. package/dist/components/Checkout/Checkout.js.map +1 -1
  8. package/dist/components/EnrollmentSummary/EnrollmentSummary.js +7 -1
  9. package/dist/components/EnrollmentSummary/EnrollmentSummary.js.map +1 -1
  10. package/dist/components/EnrollmentSummary/Messages.d.ts +5 -0
  11. package/dist/components/EnrollmentSummary/Messages.js +8 -3
  12. package/dist/components/EnrollmentSummary/Messages.js.map +1 -1
  13. package/dist/components/PaymentForm/Messages.d.ts +10 -0
  14. package/dist/components/PaymentForm/Messages.js +13 -3
  15. package/dist/components/PaymentForm/Messages.js.map +1 -1
  16. package/dist/components/PaymentForm/PaymentForm.d.ts +2 -2
  17. package/dist/components/PaymentForm/PaymentForm.js +29 -20
  18. package/dist/components/PaymentForm/PaymentForm.js.map +1 -1
  19. package/dist/components/StatusScreens/Messages.d.ts +5 -0
  20. package/dist/components/StatusScreens/Messages.js +6 -0
  21. package/dist/components/StatusScreens/Messages.js.map +1 -1
  22. package/dist/components/StatusScreens/StatusScreens.d.ts +1 -0
  23. package/dist/components/StatusScreens/StatusScreens.js +8 -1
  24. package/dist/components/StatusScreens/StatusScreens.js.map +1 -1
  25. package/dist/data/hooks/useCheckout.d.ts +6 -2
  26. package/dist/data/hooks/useCheckout.js +45 -13
  27. package/dist/data/hooks/useCheckout.js.map +1 -1
  28. package/dist/data/hooks/useCheckoutMutations.d.ts +6 -2
  29. package/dist/data/hooks/useCheckoutMutations.js +62 -31
  30. package/dist/data/hooks/useCheckoutMutations.js.map +1 -1
  31. package/dist/routes.js +2 -2
  32. package/dist/routes.js.map +1 -1
  33. package/dist/styles/components/statusScreens.scss +10 -0
  34. package/dist/types.d.ts +9 -0
  35. package/dist/types.js.map +1 -1
  36. package/dist/utils/cartApi.d.ts +4 -4
  37. package/dist/utils/cartApi.js +26 -14
  38. package/dist/utils/cartApi.js.map +1 -1
  39. package/dist/utils/provision.js +2 -2
  40. package/dist/utils/provision.js.map +1 -1
  41. package/dist/utils/queries.d.ts +1 -1
  42. package/dist/utils/queries.js +2 -0
  43. package/dist/utils/queries.js.map +1 -1
  44. package/dist/utils/stripe.d.ts +16 -0
  45. package/dist/utils/stripe.js +43 -0
  46. package/dist/utils/stripe.js.map +1 -1
  47. package/package.json +1 -1
@@ -14,14 +14,17 @@ const PaymentForm = ({ billing, setField, numberMountRef, expiryMountRef, cvcMou
14
14
  const [paymentMethod, setPaymentMethod] = useState('card');
15
15
  const [confirmed, setConfirmed] = useState(false);
16
16
  const [ukCaConfirmed, setUkCaConfirmed] = useState(false);
17
+ console.log('userCountry', userCountry);
18
+ console.log('COUNTRIES_REQUIRING_EXTRA_CONFIRM', COUNTRIES_REQUIRING_EXTRA_CONFIRM);
17
19
  const showExtraConfirm = !!userCountry && COUNTRIES_REQUIRING_EXTRA_CONFIRM.includes(userCountry);
18
- const busy = submitting || !stripeReady || !sessionReady;
19
- let buttonLabel = formatMessage(messages.confirmSubscription);
20
+ const needsStripe = paymentMethod !== 'paypal';
21
+ const busy = submitting || (needsStripe && !stripeReady) || !sessionReady;
22
+ let buttonLabel = paymentMethod === 'paypal'
23
+ ? formatMessage(messages.continueWithPaypal)
24
+ : formatMessage(messages.confirmSubscription);
20
25
  if (submitting)
21
26
  buttonLabel = formatMessage(messages.processing);
22
- else if (!sessionReady)
23
- buttonLabel = formatMessage(messages.loadingPayment);
24
- else if (!stripeReady)
27
+ else if (!sessionReady || (needsStripe && !stripeReady))
25
28
  buttonLabel = formatMessage(messages.loadingPayment);
26
29
  const isMonthly = cycleToShow === 'monthly';
27
30
  const frequency = cycleToShow === 'annual' ? 'year' : 'month';
@@ -45,14 +48,15 @@ const PaymentForm = ({ billing, setField, numberMountRef, expiryMountRef, cvcMou
45
48
  : formatMessage(messages.recurringConfirmAnnual, {
46
49
  amount: (_b = priceToShow === null || priceToShow === void 0 ? void 0 : priceToShow.amount) !== null && _b !== void 0 ? _b : '',
47
50
  });
51
+ const termsNoticeValues = {
52
+ pricing,
53
+ termsLink: formatMessage(messages.termsOfUse),
54
+ refundLink: formatMessage(messages.refundPolicy),
55
+ };
56
+ const termsText = isMonthly
57
+ ? formatMessage(messages.termsNotice, Object.assign(Object.assign({}, termsNoticeValues), { trialDays, date: trialEnd !== null && trialEnd !== void 0 ? trialEnd : '{date}' }))
58
+ : formatMessage(messages.termsNoticeAnnual, termsNoticeValues);
48
59
  const buildConsent = () => {
49
- const termsText = formatMessage(messages.termsNotice, {
50
- trialDays,
51
- date: trialEnd !== null && trialEnd !== void 0 ? trialEnd : '{date}',
52
- pricing,
53
- termsLink: formatMessage(messages.termsOfUse),
54
- refundLink: formatMessage(messages.refundPolicy),
55
- });
56
60
  const consentParts = [termsText, recurringConfirmText];
57
61
  if (showExtraConfirm) {
58
62
  consentParts.push(formatMessage(messages.ukCaResidentConfirm));
@@ -68,16 +72,21 @@ const PaymentForm = ({ billing, setField, numberMountRef, expiryMountRef, cvcMou
68
72
  link: (_jsx("a", { href: "https://support.edx.org", target: "_blank", rel: "noreferrer", className: "text-gray-700", children: formatMessage(messages.learnerHelpCenter) })),
69
73
  }) })] }), _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) })] }), _jsxs("div", { className: `payment-form__card-panel${paymentMethod === 'card' ? '' : ' payment-form__card-panel--collapsed'}`, "aria-hidden": paymentMethod !== 'card', 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: BILLING_COUNTRIES.map((country) => {
70
74
  return (_jsx("option", { value: country[0], children: country[1] }, country[0]));
71
- }) })] })] })] }), _jsx("div", { className: `my-3 payment-form__method payment-form__method--hidden${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: `my-3 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, {
72
- trialDays,
73
- date: trialEnd !== null && trialEnd !== void 0 ? trialEnd : '{date}',
74
- pricing,
75
- termsLink: (_jsx("a", { className: "text-gray-700", href: tosUrl, children: formatMessage(messages.termsOfUse) })),
76
- refundLink: (_jsx("a", { className: "text-gray-700", href: refundPolicyUrl, children: formatMessage(messages.refundPolicy) })),
77
- }) }), _jsx(Form.Group, { controlId: "recurringConfirm", className: "mb-3 payment-form__confirm", children: _jsx(Form.Checkbox, { id: "recurringConfirm", checked: confirmed, onChange: (e) => setConfirmed(e.target.checked), children: _jsx("span", { className: "text-gray-700 payment-form__confirm-label", children: recurringConfirmText }) }) }), showExtraConfirm && (_jsx(Form.Group, { controlId: "ukCaConfirm", className: "mb-4 payment-form__confirm", children: _jsx(Form.Checkbox, { id: "ukCaConfirm", checked: ukCaConfirmed, onChange: (e) => setUkCaConfirmed(e.target.checked), children: _jsx("span", { className: "text-gray-700 payment-form__confirm-label", children: formatMessage(messages.ukCaResidentConfirm) }) }) })), _jsxs(Button, { variant: "brand", className: "payment-form__submit", onClick: () => onPlaceOrder(buildConsent()), disabled: !stripeReady
75
+ }) })] })] })] }), _jsx("div", { className: `my-3 payment-form__method payment-form__method--hidden${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: `my-3 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: isMonthly
76
+ ? formatMessage(messages.termsNotice, {
77
+ trialDays,
78
+ date: trialEnd !== null && trialEnd !== void 0 ? trialEnd : '{date}',
79
+ pricing,
80
+ termsLink: (_jsx("a", { className: "text-gray-700", href: tosUrl, children: formatMessage(messages.termsOfUse) })),
81
+ refundLink: (_jsx("a", { className: "text-gray-700", href: refundPolicyUrl, children: formatMessage(messages.refundPolicy) })),
82
+ })
83
+ : formatMessage(messages.termsNoticeAnnual, {
84
+ pricing,
85
+ termsLink: (_jsx("a", { className: "text-gray-700", href: tosUrl, children: formatMessage(messages.termsOfUse) })),
86
+ refundLink: (_jsx("a", { className: "text-gray-700", href: refundPolicyUrl, children: formatMessage(messages.refundPolicy) })),
87
+ }) }), _jsx(Form.Group, { controlId: "recurringConfirm", className: "mb-3 payment-form__confirm", children: _jsx(Form.Checkbox, { id: "recurringConfirm", checked: confirmed, onChange: (e) => setConfirmed(e.target.checked), children: _jsx("span", { className: "text-gray-700 payment-form__confirm-label", children: recurringConfirmText }) }) }), showExtraConfirm && (_jsx(Form.Group, { controlId: "ukCaConfirm", className: "mb-4 payment-form__confirm", children: _jsx(Form.Checkbox, { id: "ukCaConfirm", checked: ukCaConfirmed, onChange: (e) => setUkCaConfirmed(e.target.checked), children: _jsx("span", { className: "text-gray-700 payment-form__confirm-label", children: formatMessage(messages.ukCaResidentConfirm) }) }) })), _jsxs(Button, { variant: "brand", className: "payment-form__submit", onClick: () => onPlaceOrder(buildConsent(), paymentMethod), disabled: (needsStripe && !stripeReady)
78
88
  || submitting
79
89
  || !confirmed
80
- || paymentMethod === 'paypal'
81
90
  || (showExtraConfirm && !ukCaConfirmed), children: [busy && (_jsx(Spinner, { animation: "border", size: "sm", className: "mr-2", screenReaderText: formatMessage(messages.spinnerScreenReaderText) })), buttonLabel] })] }));
82
91
  };
83
92
  export default PaymentForm;
@@ -1 +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, getAppConfig } from '@openedx/frontend-base';\nimport PaymentFields from '../PaymentFields/PaymentFields';\nimport type { BillingInfo, CheckoutConsent } from '../../types';\nimport type { PlanPrice } from '../../utils/billingDetails';\nimport { appId, COUNTRIES_REQUIRING_EXTRA_CONFIRM, BILLING_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 /** Promotional monthly price e.g. \"39\" — monthly plans only. */\n promoAmount?: string,\n /** Length of the promotional period in months. */\n promoMonths?: number,\n /** Regular monthly price after the promo period e.g. \"59\". */\n monthlyRegularAmount?: string,\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 promoAmount = '39',\n promoMonths = 3,\n monthlyRegularAmount = '59',\n}: PaymentFormProps) => {\n const { formatMessage } = useIntl();\n const config = getAppConfig(appId);\n const tosUrl = config.TOS_URL as string;\n const refundPolicyUrl = config.REFUND_POLICY_URL as string;\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 isMonthly = cycleToShow === 'monthly';\n const frequency = cycleToShow === 'annual' ? 'year' : 'month';\n const trialDays = 7;\n const pricing = isMonthly\n ? formatMessage(messages.termsPricingMonthly, {\n promoAmount,\n promoMonths,\n regularAmount: monthlyRegularAmount,\n })\n : formatMessage(messages.termsPricingAnnual, {\n amount: priceToShow?.amount ?? '',\n frequency,\n });\n const recurringConfirmText = isMonthly\n ? formatMessage(messages.recurringConfirmMonthly, {\n promoAmount,\n promoMonths,\n regularAmount: monthlyRegularAmount,\n })\n : formatMessage(messages.recurringConfirmAnnual, {\n amount: priceToShow?.amount ?? '',\n });\n\n const buildConsent = (): CheckoutConsent => {\n const termsText = formatMessage(messages.termsNotice, {\n trialDays,\n date: trialEnd ?? '{date}',\n pricing,\n termsLink: formatMessage(messages.termsOfUse),\n refundLink: formatMessage(messages.refundPolicy),\n });\n const consentParts = [termsText, recurringConfirmText];\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 {/* Keep Stripe host nodes mounted so Elements stay interactive after toggling methods. */}\n <div\n className={`payment-form__card-panel${paymentMethod === 'card' ? '' : ' payment-form__card-panel--collapsed'}`}\n aria-hidden={paymentMethod !== 'card'}\n >\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 {BILLING_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 </div>\n\n {/* ── Google Pay (hidden for now) ── */}\n <div\n className={`my-3 payment-form__method payment-form__method--hidden${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 (UI only; backend wiring pending) ── */}\n <div\n className={`my-3 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 trialDays,\n date: trialEnd ?? '{date}',\n pricing,\n termsLink: (\n <a\n className=\"text-gray-700\"\n href={tosUrl}\n >\n {formatMessage(messages.termsOfUse)}\n </a>\n ),\n refundLink: (\n <a\n className=\"text-gray-700\"\n href={refundPolicyUrl}\n >\n {formatMessage(messages.refundPolicy)}\n </a>\n ),\n })}\n </p>\n\n {/* ── Checkboxes ── */}\n <Form.Group controlId=\"recurringConfirm\" className=\"mb-3 payment-form__confirm\">\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 payment-form__confirm-label\">\n {recurringConfirmText}\n </span>\n </Form.Checkbox>\n </Form.Group>\n\n {showExtraConfirm && (\n <Form.Group controlId=\"ukCaConfirm\" className=\"mb-4 payment-form__confirm\">\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 payment-form__confirm-label\">\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 || paymentMethod === 'paypal'\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"]}
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, getAppConfig } from '@openedx/frontend-base';\nimport PaymentFields from '../PaymentFields/PaymentFields';\nimport type { BillingInfo, CheckoutConsent, PaymentMethod } from '../../types';\nimport type { PlanPrice } from '../../utils/billingDetails';\nimport { appId, COUNTRIES_REQUIRING_EXTRA_CONFIRM, BILLING_COUNTRIES } from '../../constants';\nimport messages from './Messages';\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, paymentMethod: PaymentMethod) => void,\n priceToShow: PlanPrice | null,\n cycleToShow: string,\n trialEnd?: string,\n userCountry?: string | null,\n /** Promotional monthly price e.g. \"39\" — monthly plans only. */\n promoAmount?: string,\n /** Length of the promotional period in months. */\n promoMonths?: number,\n /** Regular monthly price after the promo period e.g. \"59\". */\n monthlyRegularAmount?: string,\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 promoAmount = '39',\n promoMonths = 3,\n monthlyRegularAmount = '59',\n}: PaymentFormProps) => {\n const { formatMessage } = useIntl();\n const config = getAppConfig(appId);\n const tosUrl = config.TOS_URL as string;\n const refundPolicyUrl = config.REFUND_POLICY_URL as string;\n const [paymentMethod, setPaymentMethod] = useState<PaymentMethod>('card');\n const [confirmed, setConfirmed] = useState(false);\n const [ukCaConfirmed, setUkCaConfirmed] = useState(false);\n\n console.log('userCountry', userCountry);\n console.log('COUNTRIES_REQUIRING_EXTRA_CONFIRM', COUNTRIES_REQUIRING_EXTRA_CONFIRM);\n\n const showExtraConfirm\n = !!userCountry && COUNTRIES_REQUIRING_EXTRA_CONFIRM.includes(userCountry);\n\n const needsStripe = paymentMethod !== 'paypal';\n const busy = submitting || (needsStripe && !stripeReady) || !sessionReady;\n\n let buttonLabel = paymentMethod === 'paypal'\n ? formatMessage(messages.continueWithPaypal)\n : formatMessage(messages.confirmSubscription);\n if (submitting) buttonLabel = formatMessage(messages.processing);\n else if (!sessionReady || (needsStripe && !stripeReady)) buttonLabel = formatMessage(messages.loadingPayment);\n\n const isMonthly = cycleToShow === 'monthly';\n const frequency = cycleToShow === 'annual' ? 'year' : 'month';\n const trialDays = 7;\n const pricing = isMonthly\n ? formatMessage(messages.termsPricingMonthly, {\n promoAmount,\n promoMonths,\n regularAmount: monthlyRegularAmount,\n })\n : formatMessage(messages.termsPricingAnnual, {\n amount: priceToShow?.amount ?? '',\n frequency,\n });\n const recurringConfirmText = isMonthly\n ? formatMessage(messages.recurringConfirmMonthly, {\n promoAmount,\n promoMonths,\n regularAmount: monthlyRegularAmount,\n })\n : formatMessage(messages.recurringConfirmAnnual, {\n amount: priceToShow?.amount ?? '',\n });\n\n const termsNoticeValues = {\n pricing,\n termsLink: formatMessage(messages.termsOfUse),\n refundLink: formatMessage(messages.refundPolicy),\n };\n\n const termsText = isMonthly\n ? formatMessage(messages.termsNotice, {\n ...termsNoticeValues,\n trialDays,\n date: trialEnd ?? '{date}',\n })\n : formatMessage(messages.termsNoticeAnnual, termsNoticeValues);\n\n const buildConsent = (): CheckoutConsent => {\n const consentParts = [termsText, recurringConfirmText];\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 {/* Keep Stripe host nodes mounted so Elements stay interactive after toggling methods. */}\n <div\n className={`payment-form__card-panel${paymentMethod === 'card' ? '' : ' payment-form__card-panel--collapsed'}`}\n aria-hidden={paymentMethod !== 'card'}\n >\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 {BILLING_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 </div>\n\n {/* ── Google Pay (hidden for now) ── */}\n <div\n className={`my-3 payment-form__method payment-form__method--hidden${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={`my-3 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 {isMonthly\n ? formatMessage(messages.termsNotice, {\n trialDays,\n date: trialEnd ?? '{date}',\n pricing,\n termsLink: (\n <a\n className=\"text-gray-700\"\n href={tosUrl}\n >\n {formatMessage(messages.termsOfUse)}\n </a>\n ),\n refundLink: (\n <a\n className=\"text-gray-700\"\n href={refundPolicyUrl}\n >\n {formatMessage(messages.refundPolicy)}\n </a>\n ),\n })\n : formatMessage(messages.termsNoticeAnnual, {\n pricing,\n termsLink: (\n <a\n className=\"text-gray-700\"\n href={tosUrl}\n >\n {formatMessage(messages.termsOfUse)}\n </a>\n ),\n refundLink: (\n <a\n className=\"text-gray-700\"\n href={refundPolicyUrl}\n >\n {formatMessage(messages.refundPolicy)}\n </a>\n ),\n })}\n </p>\n\n {/* ── Checkboxes ── */}\n <Form.Group controlId=\"recurringConfirm\" className=\"mb-3 payment-form__confirm\">\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 payment-form__confirm-label\">\n {recurringConfirmText}\n </span>\n </Form.Checkbox>\n </Form.Group>\n\n {showExtraConfirm && (\n <Form.Group controlId=\"ukCaConfirm\" className=\"mb-4 payment-form__confirm\">\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 payment-form__confirm-label\">\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(), paymentMethod)}\n disabled={\n (needsStripe && !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"]}
@@ -74,6 +74,11 @@ declare const messages: {
74
74
  defaultMessage: string;
75
75
  description: string;
76
76
  };
77
+ processingSpinner: {
78
+ id: string;
79
+ defaultMessage: string;
80
+ description: string;
81
+ };
77
82
  blockedHeading: {
78
83
  id: string;
79
84
  defaultMessage: string;
@@ -76,6 +76,12 @@ const messages = defineMessages({
76
76
  defaultMessage: 'To avoid being charged, cancel your subscription by {trialEnd} from your dashboard.',
77
77
  description: 'Trial cancellation reminder at the bottom of the success screen',
78
78
  },
79
+ // ProcessingScreen
80
+ processingSpinner: {
81
+ id: 'checkout.processingScreen.spinner',
82
+ defaultMessage: 'Loading',
83
+ description: 'Screen reader text for the full-screen loader after Stripe return',
84
+ },
79
85
  // BlockedScreen
80
86
  blockedHeading: {
81
87
  id: 'checkout.blockedScreen.heading',
@@ -1 +1 @@
1
- {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/StatusScreens/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,gBAAgB;IAChB,eAAe,EAAE;QACf,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,iCAAiC;QACjD,WAAW,EAAE,qCAAqC;KACnD;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,oEAAoE;QACpF,WAAW,EAAE,mCAAmC;KACjD;IACD,4BAA4B,EAAE;QAC5B,EAAE,EAAE,8CAA8C;QAClD,cAAc,EAAE,2GAA2G;QAC3H,WAAW,EAAE,+EAA+E;KAC7F;IACD,WAAW,EAAE;QACX,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,qBAAqB;QACrC,WAAW,EAAE,0CAA0C;KACxD;IACD,WAAW,EAAE;QACX,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,qEAAqE;KACnF;IACD,WAAW,EAAE;QACX,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,yCAAyC;KACvD;IACD,aAAa,EAAE;QACb,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,sCAAsC;QACtD,WAAW,EAAE,gDAAgD;KAC9D;IACD,QAAQ,EAAE;QACR,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,qEAAqE;QACrF,WAAW,EAAE,4BAA4B;KAC1C;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,2CAA2C;QAC/C,cAAc,EAAE,uFAAuF;QACvG,WAAW,EAAE,4EAA4E;KAC1F;IACD,QAAQ,EAAE;QACR,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,uCAAuC;QACvD,WAAW,EAAE,6BAA6B;KAC3C;IACD,QAAQ,EAAE;QACR,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,qBAAqB;QACrC,WAAW,EAAE,4BAA4B;KAC1C;IACD,QAAQ,EAAE;QACR,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,uCAAuC;QACvD,WAAW,EAAE,6BAA6B;KAC3C;IACD,WAAW,EAAE;QACX,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,oBAAoB;KAClC;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,eAAe;QAC/B,WAAW,EAAE,qBAAqB;KACnC;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,qFAAqF;QACrG,WAAW,EAAE,iEAAiE;KAC/E;IAED,gBAAgB;IAChB,cAAc,EAAE;QACd,EAAE,EAAE,gCAAgC;QACpC,cAAc,EAAE,eAAe;QAC/B,WAAW,EAAE,qCAAqC;KACnD;IACD,cAAc,EAAE;QACd,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,gGAAgG;QAChH,WAAW,EAAE,oDAAoD;KAClE;IAED,sBAAsB;IACtB,oBAAoB,EAAE;QACpB,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,gBAAgB;QAChC,WAAW,EAAE,kCAAkC;KAChD;IACD,oBAAoB,EAAE;QACpB,EAAE,EAAE,6CAA6C;QACjD,cAAc,EAAE,iFAAiF;QACjG,WAAW,EAAE,gCAAgC;KAC9C;IACD,QAAQ,EAAE;QACR,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,WAAW;QAC3B,WAAW,EAAE,wBAAwB;KACtC;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n // SuccessScreen\n successHeadline: {\n id: 'checkout.successScreen.headline',\n defaultMessage: \"You're ready to begin learning.\",\n description: 'Main headline on the success screen',\n },\n successSubheadline: {\n id: 'checkout.successScreen.subheadline',\n defaultMessage: 'Your subscription is now active. Below is a summary of your order.',\n description: 'Subheadline on the success screen',\n },\n successSubheadlineWithCourse: {\n id: 'checkout.successScreen.subheadlineWithCourse',\n defaultMessage: \"Your subscription is now active (and you're enrolled in {courseLabel}). Below is a summary of your order.\",\n description: 'Subheadline on the success screen when the user enrolled in a specific course',\n },\n findACourse: {\n id: 'checkout.successScreen.findACourse',\n defaultMessage: \"Let's find a course\",\n description: 'Primary CTA button on the success screen',\n },\n startCourse: {\n id: 'checkout.successScreen.startCourse',\n defaultMessage: 'Start course',\n description: 'Primary CTA when the user enrolled in a specific course at checkout',\n },\n viewReceipt: {\n id: 'checkout.successScreen.viewReceipt',\n defaultMessage: 'View receipt',\n description: 'View receipt link on the success screen',\n },\n whatsIncluded: {\n id: 'checkout.successScreen.whatsIncluded',\n defaultMessage: \"What's included in your subscription\",\n description: 'Section heading for subscription benefits list',\n },\n benefit1: {\n id: 'checkout.successScreen.benefit1',\n defaultMessage: 'Access to graded assignments and exams in all courses you enroll in',\n description: 'First subscription benefit',\n },\n benefit1WithCourse: {\n id: 'checkout.successScreen.benefit1WithCourse',\n defaultMessage: 'Access to graded assignments and exams in {courseLabel} and all courses you enroll in',\n description: 'First subscription benefit when a specific course was enrolled at checkout',\n },\n benefit2: {\n id: 'checkout.successScreen.benefit2',\n defaultMessage: 'Shareable certificate upon completion',\n description: 'Second subscription benefit',\n },\n benefit3: {\n id: 'checkout.successScreen.benefit3',\n defaultMessage: '24/7 global support',\n description: 'Third subscription benefit',\n },\n benefit4: {\n id: 'checkout.successScreen.benefit4',\n defaultMessage: 'Early access to edX learning features',\n description: 'Fourth subscription benefit',\n },\n orderNumber: {\n id: 'checkout.successScreen.orderNumber',\n defaultMessage: 'Order number',\n description: 'Order number label',\n },\n purchaseDate: {\n id: 'checkout.successScreen.purchaseDate',\n defaultMessage: 'Purchase date',\n description: 'Purchase date label',\n },\n cancelNotice: {\n id: 'checkout.successScreen.cancelNotice',\n defaultMessage: 'To avoid being charged, cancel your subscription by {trialEnd} from your dashboard.',\n description: 'Trial cancellation reminder at the bottom of the success screen',\n },\n\n // BlockedScreen\n blockedHeading: {\n id: 'checkout.blockedScreen.heading',\n defaultMessage: 'Order blocked',\n description: 'Heading shown when order is blocked',\n },\n blockedDefault: {\n id: 'checkout.blockedScreen.defaultMessage',\n defaultMessage: 'We were unable to complete this order. Please contact support if you believe this is an error.',\n description: 'Default blocked message when no detail is provided',\n },\n\n // PaymentFailedScreen\n paymentFailedHeading: {\n id: 'checkout.paymentFailedScreen.heading',\n defaultMessage: 'Payment failed',\n description: 'Heading shown when payment fails',\n },\n paymentFailedDefault: {\n id: 'checkout.paymentFailedScreen.defaultMessage',\n defaultMessage: 'Your card could not be processed. Please check your card details and try again.',\n description: 'Default payment failed message',\n },\n tryAgain: {\n id: 'checkout.paymentFailedScreen.tryAgain',\n defaultMessage: 'Try again',\n description: 'Try again button label',\n },\n});\n\nexport default messages;\n"]}
1
+ {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/StatusScreens/Messages.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n // SuccessScreen\n successHeadline: {\n id: 'checkout.successScreen.headline',\n defaultMessage: \"You're ready to begin learning.\",\n description: 'Main headline on the success screen',\n },\n successSubheadline: {\n id: 'checkout.successScreen.subheadline',\n defaultMessage: 'Your subscription is now active. Below is a summary of your order.',\n description: 'Subheadline on the success screen',\n },\n successSubheadlineWithCourse: {\n id: 'checkout.successScreen.subheadlineWithCourse',\n defaultMessage: \"Your subscription is now active (and you're enrolled in {courseLabel}). Below is a summary of your order.\",\n description: 'Subheadline on the success screen when the user enrolled in a specific course',\n },\n findACourse: {\n id: 'checkout.successScreen.findACourse',\n defaultMessage: \"Let's find a course\",\n description: 'Primary CTA button on the success screen',\n },\n startCourse: {\n id: 'checkout.successScreen.startCourse',\n defaultMessage: 'Start course',\n description: 'Primary CTA when the user enrolled in a specific course at checkout',\n },\n viewReceipt: {\n id: 'checkout.successScreen.viewReceipt',\n defaultMessage: 'View receipt',\n description: 'View receipt link on the success screen',\n },\n whatsIncluded: {\n id: 'checkout.successScreen.whatsIncluded',\n defaultMessage: \"What's included in your subscription\",\n description: 'Section heading for subscription benefits list',\n },\n benefit1: {\n id: 'checkout.successScreen.benefit1',\n defaultMessage: 'Access to graded assignments and exams in all courses you enroll in',\n description: 'First subscription benefit',\n },\n benefit1WithCourse: {\n id: 'checkout.successScreen.benefit1WithCourse',\n defaultMessage: 'Access to graded assignments and exams in {courseLabel} and all courses you enroll in',\n description: 'First subscription benefit when a specific course was enrolled at checkout',\n },\n benefit2: {\n id: 'checkout.successScreen.benefit2',\n defaultMessage: 'Shareable certificate upon completion',\n description: 'Second subscription benefit',\n },\n benefit3: {\n id: 'checkout.successScreen.benefit3',\n defaultMessage: '24/7 global support',\n description: 'Third subscription benefit',\n },\n benefit4: {\n id: 'checkout.successScreen.benefit4',\n defaultMessage: 'Early access to edX learning features',\n description: 'Fourth subscription benefit',\n },\n orderNumber: {\n id: 'checkout.successScreen.orderNumber',\n defaultMessage: 'Order number',\n description: 'Order number label',\n },\n purchaseDate: {\n id: 'checkout.successScreen.purchaseDate',\n defaultMessage: 'Purchase date',\n description: 'Purchase date label',\n },\n cancelNotice: {\n id: 'checkout.successScreen.cancelNotice',\n defaultMessage: 'To avoid being charged, cancel your subscription by {trialEnd} from your dashboard.',\n description: 'Trial cancellation reminder at the bottom of the success screen',\n },\n\n // ProcessingScreen\n processingSpinner: {\n id: 'checkout.processingScreen.spinner',\n defaultMessage: 'Loading',\n description: 'Screen reader text for the full-screen loader after Stripe return',\n },\n\n // BlockedScreen\n blockedHeading: {\n id: 'checkout.blockedScreen.heading',\n defaultMessage: 'Order blocked',\n description: 'Heading shown when order is blocked',\n },\n blockedDefault: {\n id: 'checkout.blockedScreen.defaultMessage',\n defaultMessage: 'We were unable to complete this order. Please contact support if you believe this is an error.',\n description: 'Default blocked message when no detail is provided',\n },\n\n // PaymentFailedScreen\n paymentFailedHeading: {\n id: 'checkout.paymentFailedScreen.heading',\n defaultMessage: 'Payment failed',\n description: 'Heading shown when payment fails',\n },\n paymentFailedDefault: {\n id: 'checkout.paymentFailedScreen.defaultMessage',\n defaultMessage: 'Your card could not be processed. Please check your card details and try again.',\n description: 'Default payment failed message',\n },\n tryAgain: {\n id: 'checkout.paymentFailedScreen.tryAgain',\n defaultMessage: 'Try again',\n description: 'Try again button label',\n },\n});\n\nexport default messages;\n"]}
@@ -12,6 +12,7 @@ export interface SuccessScreenProps {
12
12
  courseLabel?: string | null;
13
13
  }
14
14
  export declare const SuccessScreen: ({ subscriptionBiId, orderNumber, purchaseDate, trialEnd, onFindCourse, courseLabel, }: SuccessScreenProps) => import("react/jsx-runtime").JSX.Element;
15
+ export declare const ProcessingScreen: () => import("react/jsx-runtime").JSX.Element;
15
16
  export interface BlockedScreenProps {
16
17
  detail?: string;
17
18
  }
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Icon } from '@openedx/paragon';
2
+ import { Button, Icon, Spinner } from '@openedx/paragon';
3
3
  import { Error as ErrorIcon, Block } from '@openedx/paragon/icons';
4
4
  import { useIntl } from '@openedx/frontend-base';
5
5
  import messages from './Messages';
@@ -14,6 +14,13 @@ export const SuccessScreen = ({ subscriptionBiId, orderNumber, purchaseDate, tri
14
14
  ? formatMessage(messages.benefit1WithCourse, { courseLabel })
15
15
  : formatMessage(messages.benefit1) }), _jsx("li", { className: "mb-2", children: formatMessage(messages.benefit2) }), _jsx("li", { className: "mb-2", children: formatMessage(messages.benefit3) }), _jsx("li", { className: "mb-2", children: formatMessage(messages.benefit4) })] })] }), _jsxs("div", { className: "col-12 col-md-5 mb-4", children: [displayOrderNumber && (_jsxs("div", { className: "mb-3", children: [_jsx("p", { className: "font-weight-bold mb-0", children: formatMessage(messages.orderNumber) }), _jsx("p", { className: "text-muted mb-0", children: displayOrderNumber })] })), purchaseDate && (_jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0", children: formatMessage(messages.purchaseDate) }), _jsx("p", { className: "text-muted mb-0", children: purchaseDate })] }))] })] }), _jsx("hr", {}), trialEnd && (_jsx("p", { className: "text-muted small mt-3", children: formatMessage(messages.cancelNotice, { trialEnd }) }))] }));
16
16
  };
17
+ // ---------------------------------------------------------------------------
18
+ // ProcessingScreen — full-screen loader while confirming a 3DS return
19
+ // ---------------------------------------------------------------------------
20
+ export const ProcessingScreen = () => {
21
+ const { formatMessage } = useIntl();
22
+ return (_jsx("div", { className: "processing-screen", role: "status", "aria-live": "polite", children: _jsx(Spinner, { animation: "border", screenReaderText: formatMessage(messages.processingSpinner) }) }));
23
+ };
17
24
  export const BlockedScreen = ({ detail }) => {
18
25
  const { formatMessage } = useIntl();
19
26
  return (_jsxs("div", { className: "blocked-screen text-center py-5", children: [_jsx(Icon, { src: Block, className: "blocked-screen__icon text-danger mb-3" }), _jsx("h3", { className: "font-weight-bold", children: formatMessage(messages.blockedHeading) }), _jsx("p", { className: "text-muted", children: detail || formatMessage(messages.blockedDefault) })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"StatusScreens.js","sourceRoot":"","sources":["../../../src/components/StatusScreens/StatusScreens.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport { Button, Icon } from '@openedx/paragon';\nimport { Error as ErrorIcon, Block } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport messages from './Messages';\n\n// ---------------------------------------------------------------------------\n// SuccessScreen\n// ---------------------------------------------------------------------------\n\nexport interface SuccessScreenProps {\n subscriptionBiId?: string,\n orderNumber?: string,\n purchaseDate?: string,\n trialStart?: string,\n trialEnd?: string,\n onFindCourse?: () => void,\n cardLast4?: string,\n priceToShow?: string,\n cycleToShow?: string,\n /** e.g. \"StanfordOnline: Algorithms: Design and Analysis\" — enables course-aware copy. */\n courseLabel?: string | null,\n}\n\nexport const SuccessScreen = ({\n subscriptionBiId,\n orderNumber,\n purchaseDate,\n trialEnd,\n onFindCourse,\n courseLabel = null,\n}: SuccessScreenProps) => {\n const { formatMessage } = useIntl();\n\n const displayOrderNumber = orderNumber ?? subscriptionBiId ?? '—';\n const hasCourse = !!courseLabel;\n\n return (\n <div className=\"success-screen\">\n <h1 className=\"success-screen__headline font-weight-bold mb-2\">\n {formatMessage(messages.successHeadline)}\n </h1>\n\n <p className=\"text-muted mb-4\">\n {hasCourse\n ? formatMessage(messages.successSubheadlineWithCourse, { courseLabel })\n : formatMessage(messages.successSubheadline)}\n </p>\n\n {/* ── CTAs ── */}\n <div className=\"success-screen__ctas d-flex align-items-center mb-5\">\n <Button\n variant=\"brand\"\n className=\"success-screen__cta-primary mr-3\"\n onClick={onFindCourse}\n >\n {formatMessage(hasCourse ? messages.startCourse : messages.findACourse)}\n </Button>\n {/* View receipt button hidden temporarily */}\n </div>\n\n {/* ── Two column: benefits + order details ── */}\n <div className=\"row\">\n <div className=\"col-12 col-md-7 mb-4\">\n <h2 className=\"h5 font-weight-bold mb-3\">\n {formatMessage(messages.whatsIncluded)}\n </h2>\n <ul className=\"success-screen__benefits pl-4\">\n <li className=\"mb-2\">\n {hasCourse\n ? formatMessage(messages.benefit1WithCourse, { courseLabel })\n : formatMessage(messages.benefit1)}\n </li>\n <li className=\"mb-2\">{formatMessage(messages.benefit2)}</li>\n <li className=\"mb-2\">{formatMessage(messages.benefit3)}</li>\n <li className=\"mb-2\">{formatMessage(messages.benefit4)}</li>\n </ul>\n </div>\n\n <div className=\"col-12 col-md-5 mb-4\">\n {displayOrderNumber && (\n <div className=\"mb-3\">\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.orderNumber)}\n </p>\n <p className=\"text-muted mb-0\">{displayOrderNumber}</p>\n </div>\n )}\n {purchaseDate && (\n <div>\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.purchaseDate)}\n </p>\n <p className=\"text-muted mb-0\">{purchaseDate}</p>\n </div>\n )}\n </div>\n </div>\n\n <hr />\n\n {/* ── Trial cancellation notice ── */}\n {trialEnd && (\n <p className=\"text-muted small mt-3\">\n {formatMessage(messages.cancelNotice, { trialEnd })}\n </p>\n )}\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// BlockedScreen\n// ---------------------------------------------------------------------------\n\nexport interface BlockedScreenProps {\n detail?: string,\n}\n\nexport const BlockedScreen = ({ detail }: BlockedScreenProps) => {\n const { formatMessage } = useIntl();\n return (\n <div className=\"blocked-screen text-center py-5\">\n <Icon src={Block} className=\"blocked-screen__icon text-danger mb-3\" />\n <h3 className=\"font-weight-bold\">\n {formatMessage(messages.blockedHeading)}\n </h3>\n <p className=\"text-muted\">\n {detail || formatMessage(messages.blockedDefault)}\n </p>\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// PaymentFailedScreen\n// ---------------------------------------------------------------------------\n\nexport interface PaymentFailedScreenProps {\n detail?: string,\n onBack: () => void,\n}\n\nexport const PaymentFailedScreen = ({\n detail,\n onBack,\n}: PaymentFailedScreenProps) => {\n const { formatMessage } = useIntl();\n return (\n <div className=\"payment-failed-screen text-center py-4\">\n <Icon\n src={ErrorIcon}\n className=\"payment-failed-screen__icon text-danger mb-3\"\n />\n <h3 className=\"font-weight-bold mb-2\">\n {formatMessage(messages.paymentFailedHeading)}\n </h3>\n <p className=\"payment-failed-screen__detail text-muted mb-4\">\n {detail || formatMessage(messages.paymentFailedDefault)}\n </p>\n <Button variant=\"brand\" className=\"w-100\" onClick={onBack}>\n {formatMessage(messages.tryAgain)}\n </Button>\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"StatusScreens.js","sourceRoot":"","sources":["../../../src/components/StatusScreens/StatusScreens.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport { Button, Icon, Spinner } from '@openedx/paragon';\nimport { Error as ErrorIcon, Block } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport messages from './Messages';\n\n// ---------------------------------------------------------------------------\n// SuccessScreen\n// ---------------------------------------------------------------------------\n\nexport interface SuccessScreenProps {\n subscriptionBiId?: string,\n orderNumber?: string,\n purchaseDate?: string,\n trialStart?: string,\n trialEnd?: string,\n onFindCourse?: () => void,\n cardLast4?: string,\n priceToShow?: string,\n cycleToShow?: string,\n /** e.g. \"StanfordOnline: Algorithms: Design and Analysis\" — enables course-aware copy. */\n courseLabel?: string | null,\n}\n\nexport const SuccessScreen = ({\n subscriptionBiId,\n orderNumber,\n purchaseDate,\n trialEnd,\n onFindCourse,\n courseLabel = null,\n}: SuccessScreenProps) => {\n const { formatMessage } = useIntl();\n\n const displayOrderNumber = orderNumber ?? subscriptionBiId ?? '—';\n const hasCourse = !!courseLabel;\n\n return (\n <div className=\"success-screen\">\n <h1 className=\"success-screen__headline font-weight-bold mb-2\">\n {formatMessage(messages.successHeadline)}\n </h1>\n\n <p className=\"text-muted mb-4\">\n {hasCourse\n ? formatMessage(messages.successSubheadlineWithCourse, { courseLabel })\n : formatMessage(messages.successSubheadline)}\n </p>\n\n {/* ── CTAs ── */}\n <div className=\"success-screen__ctas d-flex align-items-center mb-5\">\n <Button\n variant=\"brand\"\n className=\"success-screen__cta-primary mr-3\"\n onClick={onFindCourse}\n >\n {formatMessage(hasCourse ? messages.startCourse : messages.findACourse)}\n </Button>\n {/* View receipt button hidden temporarily */}\n </div>\n\n {/* ── Two column: benefits + order details ── */}\n <div className=\"row\">\n <div className=\"col-12 col-md-7 mb-4\">\n <h2 className=\"h5 font-weight-bold mb-3\">\n {formatMessage(messages.whatsIncluded)}\n </h2>\n <ul className=\"success-screen__benefits pl-4\">\n <li className=\"mb-2\">\n {hasCourse\n ? formatMessage(messages.benefit1WithCourse, { courseLabel })\n : formatMessage(messages.benefit1)}\n </li>\n <li className=\"mb-2\">{formatMessage(messages.benefit2)}</li>\n <li className=\"mb-2\">{formatMessage(messages.benefit3)}</li>\n <li className=\"mb-2\">{formatMessage(messages.benefit4)}</li>\n </ul>\n </div>\n\n <div className=\"col-12 col-md-5 mb-4\">\n {displayOrderNumber && (\n <div className=\"mb-3\">\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.orderNumber)}\n </p>\n <p className=\"text-muted mb-0\">{displayOrderNumber}</p>\n </div>\n )}\n {purchaseDate && (\n <div>\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.purchaseDate)}\n </p>\n <p className=\"text-muted mb-0\">{purchaseDate}</p>\n </div>\n )}\n </div>\n </div>\n\n <hr />\n\n {/* ── Trial cancellation notice ── */}\n {trialEnd && (\n <p className=\"text-muted small mt-3\">\n {formatMessage(messages.cancelNotice, { trialEnd })}\n </p>\n )}\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// ProcessingScreen — full-screen loader while confirming a 3DS return\n// ---------------------------------------------------------------------------\n\nexport const ProcessingScreen = () => {\n const { formatMessage } = useIntl();\n return (\n <div className=\"processing-screen\" role=\"status\" aria-live=\"polite\">\n <Spinner\n animation=\"border\"\n screenReaderText={formatMessage(messages.processingSpinner)}\n />\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// BlockedScreen\n// ---------------------------------------------------------------------------\n\nexport interface BlockedScreenProps {\n detail?: string,\n}\n\nexport const BlockedScreen = ({ detail }: BlockedScreenProps) => {\n const { formatMessage } = useIntl();\n return (\n <div className=\"blocked-screen text-center py-5\">\n <Icon src={Block} className=\"blocked-screen__icon text-danger mb-3\" />\n <h3 className=\"font-weight-bold\">\n {formatMessage(messages.blockedHeading)}\n </h3>\n <p className=\"text-muted\">\n {detail || formatMessage(messages.blockedDefault)}\n </p>\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// PaymentFailedScreen\n// ---------------------------------------------------------------------------\n\nexport interface PaymentFailedScreenProps {\n detail?: string,\n onBack: () => void,\n}\n\nexport const PaymentFailedScreen = ({\n detail,\n onBack,\n}: PaymentFailedScreenProps) => {\n const { formatMessage } = useIntl();\n return (\n <div className=\"payment-failed-screen text-center py-4\">\n <Icon\n src={ErrorIcon}\n className=\"payment-failed-screen__icon text-danger mb-3\"\n />\n <h3 className=\"font-weight-bold mb-2\">\n {formatMessage(messages.paymentFailedHeading)}\n </h3>\n <p className=\"payment-failed-screen__detail text-muted mb-4\">\n {detail || formatMessage(messages.paymentFailedDefault)}\n </p>\n <Button variant=\"brand\" className=\"w-100\" onClick={onBack}>\n {formatMessage(messages.tryAgain)}\n </Button>\n </div>\n );\n};\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { Ref } from 'react';
2
2
  import type { BillingCycle } from '../../types';
3
- import type { Status, Phase, BillingInfo, BillingPlan, CheckoutConsent } from '../../types';
3
+ import type { Status, Phase, BillingInfo, BillingPlan, CheckoutConsent, PaymentMethod } from '../../types';
4
4
  export interface UseCheckoutOptions {
5
5
  apiUrl?: string;
6
6
  edxSessionUrl?: string;
@@ -13,6 +13,8 @@ export interface UseCheckoutOptions {
13
13
  * When omitted, read from ?course_run_key=.
14
14
  */
15
15
  courseRunKey?: string | null;
16
+ /** Override only for tests; production performs a full-page browser redirect. */
17
+ redirectToUrl?: (url: string) => void;
16
18
  }
17
19
  export interface UseCheckoutResult {
18
20
  phase: Phase;
@@ -38,7 +40,9 @@ export interface UseCheckoutResult {
38
40
  submitting: boolean;
39
41
  /** True from Place order click until a terminal status is reached. */
40
42
  processingSubscription: boolean;
41
- placeOrder: (consent: CheckoutConsent) => void;
43
+ /** True when Stripe sent the learner back with ?cart_bi_id=. */
44
+ returningFromStripe: boolean;
45
+ placeOrder: (consent: CheckoutConsent, paymentMethod?: PaymentMethod) => void;
42
46
  /** Billing plans fetched on mount — includes prices for display in CartSummary. */
43
47
  billingPlans: BillingPlan[];
44
48
  /** Live active subscriber count for display in CartSummary. */
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
11
11
  import { createCheckoutClient } from '../../utils/checkout';
12
12
  import { getPlanFromUrl, writePlanQueryParam } from '../../utils/planQuery';
13
- import { loadStripe } from '../../utils/stripe';
13
+ import { getReturnedCartBiIdFromUrl, loadStripe } from '../../utils/stripe';
14
14
  import { STATUS, STATUS_LABELS, STRIPE_ELEMENT_OPTS, phaseFor, DEFAULTS, } from '../../constants';
15
15
  import { useCartStatusQuery, useSubscriptionIdQuery, useBillingPlansQuery, useActiveSubscriptionsQuery, useViaxSessionQuery, useCourseDetailQuery, } from './useCheckoutQueries';
16
16
  import { useCheckoutMutations } from './useCheckoutMutations';
@@ -33,9 +33,12 @@ function getCourseRunKeyFromUrl() {
33
33
  return match[1].trim() || null;
34
34
  }
35
35
  }
36
+ const redirectBrowser = (url) => {
37
+ window.location.assign(url);
38
+ };
36
39
  export default function useCheckout(options = {}) {
37
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
38
- const { apiUrl = DEFAULTS.apiUrl, edxSessionUrl, stripePublishableKey, onComplete, userCountry = null, courseRunKey: courseRunKeyProp, } = options;
40
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
41
+ const { apiUrl = DEFAULTS.apiUrl, edxSessionUrl, stripePublishableKey, onComplete, userCountry = null, courseRunKey: courseRunKeyProp, redirectToUrl = redirectBrowser, } = options;
39
42
  const courseRunKey = useMemo(() => courseRunKeyProp !== null && courseRunKeyProp !== void 0 ? courseRunKeyProp : getCourseRunKeyFromUrl(), [courseRunKeyProp]);
40
43
  const client = useMemo(() => {
41
44
  if (!apiUrl)
@@ -67,7 +70,9 @@ export default function useCheckout(options = {}) {
67
70
  firstName: '', lastName: '', city: '', zip: '', country: 'US', line1: '', state: '',
68
71
  });
69
72
  const [selectedCycle, setSelectedCycle] = useState(getPlanFromUrl);
70
- const [processingSubscription, setProcessingSubscription] = useState(false);
73
+ // Set when Stripe returns from a 3DS challenge; cleared once a new attempt starts.
74
+ const [returnedCartBiId, setReturnedCartBiId] = useState(getReturnedCartBiIdFromUrl);
75
+ const [processingSubscription, setProcessingSubscription] = useState(() => getReturnedCartBiIdFromUrl() !== null);
71
76
  const setCycle = useCallback((cycle) => {
72
77
  setSelectedCycle(cycle);
73
78
  writePlanQueryParam(cycle);
@@ -115,6 +120,7 @@ export default function useCheckout(options = {}) {
115
120
  }, [assignCardHost]);
116
121
  const mountedRef = useRef(true);
117
122
  const completedRef = useRef(false);
123
+ const redirectedUrlRef = useRef(null);
118
124
  const sessionRef = useRef(null);
119
125
  useEffect(() => {
120
126
  var _a;
@@ -137,12 +143,16 @@ export default function useCheckout(options = {}) {
137
143
  stripeInstanceRef.current = stripe;
138
144
  }, [stripe]);
139
145
  // --- server state ---
140
- const statusQuery = useCartStatusQuery(client, (_a = cart === null || cart === void 0 ? void 0 : cart.biId) !== null && _a !== void 0 ? _a : null);
146
+ // Poll the cart created in this session, or the one Stripe just returned
147
+ // from. The returned cart waits for the viax session so the status request
148
+ // carries a valid token.
149
+ const cartBiId = (_a = cart === null || cart === void 0 ? void 0 : cart.biId) !== null && _a !== void 0 ? _a : (sessionReady ? returnedCartBiId : null);
150
+ const statusQuery = useCartStatusQuery(client, cartBiId);
141
151
  const status = (_c = (_b = statusQuery.data) === null || _b === void 0 ? void 0 : _b.status) !== null && _c !== void 0 ? _c : null;
142
152
  const lastComment = (_e = (_d = statusQuery.data) === null || _d === void 0 ? void 0 : _d.lastComment) !== null && _e !== void 0 ? _e : '';
143
153
  // Phase is 'ready' until a cart exists, then driven by bpStatus.
144
- const phase = cart ? phaseFor(status, status === STATUS.IN_CART) : 'ready';
145
- const subscriptionIdQuery = useSubscriptionIdQuery(client, (_f = cart === null || cart === void 0 ? void 0 : cart.biId) !== null && _f !== void 0 ? _f : null, status === STATUS.SUBSCRIPTION_CREATED);
154
+ const phase = cartBiId ? phaseFor(status, status === STATUS.IN_CART) : 'ready';
155
+ const subscriptionIdQuery = useSubscriptionIdQuery(client, cartBiId, status === STATUS.SUBSCRIPTION_CREATED);
146
156
  // --- mutations ---
147
157
  const { placeOrder: placeMutation } = useCheckoutMutations({
148
158
  client,
@@ -248,14 +258,35 @@ export default function useCheckout(options = {}) {
248
258
  if (onComplete)
249
259
  onComplete((_b = (_a = subscriptionIdQuery.data) === null || _a === void 0 ? void 0 : _a.biId) !== null && _b !== void 0 ? _b : null);
250
260
  }, [status, subscriptionIdQuery.data, onComplete]);
261
+ // Stripe and PayPal both authenticate the buyer on a hosted page before
262
+ // returning the browser to checkout, so this must be a full-page
263
+ // navigation (not an API request) for either flow.
264
+ useEffect(() => {
265
+ var _a, _b;
266
+ const redirectUrl = ((_a = statusQuery.data) === null || _a === void 0 ? void 0 : _a.stripeConfirmationRedirectUrl)
267
+ || ((_b = statusQuery.data) === null || _b === void 0 ? void 0 : _b.paypalApprovalUrl);
268
+ if (status !== STATUS.STRIPE_CONFIRMATION_REQUIRED
269
+ || !redirectUrl
270
+ || redirectedUrlRef.current === redirectUrl)
271
+ return;
272
+ redirectedUrlRef.current = redirectUrl;
273
+ redirectToUrl(redirectUrl);
274
+ }, [
275
+ status,
276
+ (_f = statusQuery.data) === null || _f === void 0 ? void 0 : _f.stripeConfirmationRedirectUrl,
277
+ (_g = statusQuery.data) === null || _g === void 0 ? void 0 : _g.paypalApprovalUrl,
278
+ redirectToUrl,
279
+ ]);
251
280
  // --- actions ---
252
- const placeOrder = useCallback((consent) => {
281
+ const placeOrder = useCallback((consent, paymentMethod = 'card') => {
253
282
  setError('');
254
283
  setCardError('');
255
284
  if (phase === 'failed')
256
285
  setCart(null);
286
+ // Each attempt creates its own cart, so stop reporting on the returned one.
287
+ setReturnedCartBiId(null);
257
288
  setProcessingSubscription(true);
258
- placeMutation.mutate(consent);
289
+ placeMutation.mutate({ consent, paymentMethod });
259
290
  }, [placeMutation, phase]);
260
291
  // Turn off processingSubscription once we reach a terminal status.
261
292
  useEffect(() => {
@@ -283,7 +314,7 @@ export default function useCheckout(options = {}) {
283
314
  clearError: () => setError(''),
284
315
  cardError,
285
316
  lastComment,
286
- subscriptionDetails: (_g = subscriptionIdQuery.data) !== null && _g !== void 0 ? _g : null,
317
+ subscriptionDetails: (_h = subscriptionIdQuery.data) !== null && _h !== void 0 ? _h : null,
287
318
  billing,
288
319
  setField,
289
320
  numberMountRef,
@@ -291,14 +322,15 @@ export default function useCheckout(options = {}) {
291
322
  cvcMountRef,
292
323
  submitting: placeMutation.isPending,
293
324
  processingSubscription,
325
+ returningFromStripe: returnedCartBiId !== null,
294
326
  placeOrder,
295
- billingPlans: (_j = (_h = billingPlansQuery.data) === null || _h === void 0 ? void 0 : _h.plans) !== null && _j !== void 0 ? _j : [],
296
- activeSubscriberCount: (_l = (_k = activeSubscriptionsQuery.data) === null || _k === void 0 ? void 0 : _k.active) !== null && _l !== void 0 ? _l : null,
327
+ billingPlans: (_k = (_j = billingPlansQuery.data) === null || _j === void 0 ? void 0 : _j.plans) !== null && _k !== void 0 ? _k : [],
328
+ activeSubscriberCount: (_m = (_l = activeSubscriptionsQuery.data) === null || _l === void 0 ? void 0 : _l.active) !== null && _m !== void 0 ? _m : null,
297
329
  selectedCycle,
298
330
  setCycle,
299
331
  sessionReady,
300
332
  userCountry,
301
- course: (_m = courseDetailQuery.data) !== null && _m !== void 0 ? _m : null,
333
+ course: (_o = courseDetailQuery.data) !== null && _o !== void 0 ? _o : null,
302
334
  };
303
335
  }
304
336
  //# sourceMappingURL=useCheckout.js.map