@edx/frontend-app-alacarte-checkout 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Checkout/Checkout.js +29 -2
- package/dist/components/Checkout/Checkout.js.map +1 -1
- package/dist/components/ExpressCheckout/ExpressCheckout.d.ts +20 -1
- package/dist/components/ExpressCheckout/ExpressCheckout.js +77 -2
- package/dist/components/ExpressCheckout/ExpressCheckout.js.map +1 -1
- package/dist/components/PaymentForm/PaymentForm.d.ts +8 -2
- package/dist/components/PaymentForm/PaymentForm.js +14 -2
- package/dist/components/PaymentForm/PaymentForm.js.map +1 -1
- package/dist/data/hooks/useAlacarteCheckout.d.ts +9 -0
- package/dist/data/hooks/useAlacarteCheckout.js +25 -19
- package/dist/data/hooks/useAlacarteCheckout.js.map +1 -1
- package/dist/data/hooks/useCheckout.d.ts +3 -1
- package/dist/data/hooks/useCheckout.js +1 -0
- package/dist/data/hooks/useCheckout.js.map +1 -1
- package/dist/styles/components/expressCheckout.scss +6 -1
- package/dist/types.d.ts +38 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/provision.js +21 -3
- package/dist/utils/provision.js.map +1 -1
- package/dist/utils/stripeCard.d.ts +9 -0
- package/dist/utils/stripeCard.js +15 -0
- package/dist/utils/stripeCard.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
11
|
var t = {};
|
|
3
12
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -10,7 +19,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
19
|
return t;
|
|
11
20
|
};
|
|
12
21
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
-
import { useEffect, useState } from 'react';
|
|
22
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
14
23
|
import { Alert, Col, Row } from '@openedx/paragon';
|
|
15
24
|
import { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';
|
|
16
25
|
import { useIntl } from '@openedx/frontend-base';
|
|
@@ -27,6 +36,7 @@ import AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TE
|
|
|
27
36
|
import { getTrialDateRange } from '../../utils';
|
|
28
37
|
import { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';
|
|
29
38
|
import { appliedDiscountPercent } from '../../utils/discount';
|
|
39
|
+
import { confirmPaymentWithMethodId } from '../../utils/stripeCard';
|
|
30
40
|
import { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';
|
|
31
41
|
import messages from './Messages';
|
|
32
42
|
// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a
|
|
@@ -75,6 +85,23 @@ const Checkout = (_a) => {
|
|
|
75
85
|
window.location.href = homeUrl;
|
|
76
86
|
}
|
|
77
87
|
}, [alacarte.phase, homeUrl]);
|
|
88
|
+
// Express checkout (Apple Pay / Google Pay): runs the full place-order flow
|
|
89
|
+
// using a wallet-provided PaymentMethod id instead of the mounted card element.
|
|
90
|
+
const handleExpressPayment = useCallback((paymentMethodId, walletCountry) => __awaiter(void 0, void 0, void 0, function* () {
|
|
91
|
+
if (!c.stripe)
|
|
92
|
+
throw new Error('Stripe not loaded');
|
|
93
|
+
const stripeInstance = c.stripe;
|
|
94
|
+
yield alacarte.placeOrderAsync({
|
|
95
|
+
billing: Object.assign(Object.assign({}, c.billing), { country: c.billing.country || walletCountry || c.userCountry || '' }),
|
|
96
|
+
confirmCardPayment: (clientSecret) => __awaiter(void 0, void 0, void 0, function* () {
|
|
97
|
+
yield confirmPaymentWithMethodId({
|
|
98
|
+
stripe: stripeInstance,
|
|
99
|
+
clientSecret,
|
|
100
|
+
paymentMethodId,
|
|
101
|
+
});
|
|
102
|
+
}),
|
|
103
|
+
});
|
|
104
|
+
}), [alacarte, c.billing, c.userCountry, c.stripe]);
|
|
78
105
|
const showProcessing = c.returningFromStripe
|
|
79
106
|
&& c.processingSubscription
|
|
80
107
|
&& c.phase !== 'success'
|
|
@@ -169,7 +196,7 @@ const Checkout = (_a) => {
|
|
|
169
196
|
confirmCardPayment: c.confirmCardPayment,
|
|
170
197
|
});
|
|
171
198
|
}
|
|
172
|
-
: c.placeOrder, trialEnd: selectedCycle === 'monthly' ? displayEnd : undefined, priceToShow: null, cycleToShow: selectedCycle === 'monthly' ? 'monthly' : 'annual', userCountry: c.userCountry, geolocationUrl: props.geolocationUrl, alacarteLine: alacarte.line })] })), c.phase === 'success' && (_jsx(SuccessScreen, { subscriptionBiId: ((_l = c.subscriptionDetails) === null || _l === void 0 ? void 0 : _l.biId) || '', cardLast4: ((_m = c.subscriptionDetails) === null || _m === void 0 ? void 0 : _m.cardLast4) || '', purchaseDate: displayStart, courseLabel: courseLabel, onFindCourse: handleStartCourse })), c.phase === 'blocked' && (_jsx(BlockedScreen, { detail: c.lastComment }))] }) }), _jsx(Col, { xs: 12, lg: 5, xxl: 3, className: "payment__summary", children: c.phase === 'success' ? (_jsx(EnrollmentSummary, { course: c.course, onStartCourse: handleStartCourse })) : (_jsx(CartSummary, { defaultCycle: selectedCycle, onCycleChange: c.processingSubscription ? undefined : c.setCycle, course: courseForSummary, alacarteLine: alacarte.line, programDetail: alacarteProgramDetail, promoCode: isAlacarteReady ? {
|
|
199
|
+
: c.placeOrder, trialEnd: selectedCycle === 'monthly' ? displayEnd : undefined, priceToShow: null, cycleToShow: selectedCycle === 'monthly' ? 'monthly' : 'annual', userCountry: c.userCountry, geolocationUrl: props.geolocationUrl, alacarteLine: alacarte.line, stripe: c.stripe, expressCountry: c.billing.country || c.userCountry || 'US', onExpressPayment: isAlacarteReady ? handleExpressPayment : undefined })] })), c.phase === 'success' && (_jsx(SuccessScreen, { subscriptionBiId: ((_l = c.subscriptionDetails) === null || _l === void 0 ? void 0 : _l.biId) || '', cardLast4: ((_m = c.subscriptionDetails) === null || _m === void 0 ? void 0 : _m.cardLast4) || '', purchaseDate: displayStart, courseLabel: courseLabel, onFindCourse: handleStartCourse })), c.phase === 'blocked' && (_jsx(BlockedScreen, { detail: c.lastComment }))] }) }), _jsx(Col, { xs: 12, lg: 5, xxl: 3, className: "payment__summary", children: c.phase === 'success' ? (_jsx(EnrollmentSummary, { course: c.course, onStartCourse: handleStartCourse })) : (_jsx(CartSummary, { defaultCycle: selectedCycle, onCycleChange: c.processingSubscription ? undefined : c.setCycle, course: courseForSummary, alacarteLine: alacarte.line, programDetail: alacarteProgramDetail, promoCode: isAlacarteReady ? {
|
|
173
200
|
value: promoCodeValue,
|
|
174
201
|
onChange: (next) => {
|
|
175
202
|
setPromoCodeValue(next);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useState } from 'react';\nimport { Alert, Col, Row } from '@openedx/paragon';\nimport { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';\nimport { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport AlacarteNotice from '../AlacarteNotice/AlacarteNotice';\nimport AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';\nimport AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel\nimport { getTrialDateRange } from '@src/utils';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { appliedDiscountPercent } from '../../utils/discount';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle, CourseDetail } from '../../types';\nimport type { ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\n// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a\n// banner instead so the case is visible while the flow is being verified.\n// Flip back to true to restore the redirect.\nconst REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** Endpoint used to look up the representative IP for a selected country. */\n geolocationUrl?: string,\n /** Learner dashboard / order history URL for confirmation CTAs. */\n orderHistoryUrl?: string,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n orderHistoryUrl,\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });\n const c = useCheckout(props);\n const [promoCodeValue, setPromoCodeValue] = useState('');\n const [dismissedCouponAlert, setDismissedCouponAlert] = useState<string | null>(null);\n // Last code we successfully priced — drives the \"Save {percent}% with code\n // {code}\" hint on the empty field while a coupon is not yet applied.\n const [advertisedOffer, setAdvertisedOffer] = useState<{ code: string, percent: number } | null>(null);\n const isAlacarteReady = alacarte.phase === 'ready' && !!alacarte.checkout;\n const appliedCouponCode = alacarte.checkout?.couponCode?.trim()\n ? alacarte.checkout.couponCode.trim()\n : null;\n const discountPercent = appliedDiscountPercent(alacarte.line);\n const appliedDiscount = appliedCouponCode && alacarte.line?.discount && discountPercent != null\n ? {\n percent: discountPercent,\n amount: alacarte.line.discount.amount,\n currency: alacarte.line.discount.currency,\n }\n : null;\n\n // Keep the coupon field in sync when a code is applied via URL or API.\n useEffect(() => {\n if (alacarte.checkout?.couponCode) {\n setPromoCodeValue(alacarte.checkout.couponCode);\n }\n }, [alacarte.checkout?.couponCode]);\n\n useEffect(() => {\n if (appliedCouponCode && discountPercent != null) {\n setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });\n }\n }, [appliedCouponCode, discountPercent]);\n\n // No query params and no active cart — nothing to check out, send the\n // learner home rather than showing a placeholder.\n useEffect(() => {\n if (REDIRECT_ON_NO_ACTIVE_CHECKOUT && alacarte.phase === 'noActiveCheckout') {\n window.location.href = homeUrl;\n }\n }, [alacarte.phase, homeUrl]);\n\n const showProcessing\n = c.returningFromStripe\n && c.processingSubscription\n && c.phase !== 'success'\n && c.phase !== 'blocked'\n && c.phase !== 'failed';\n const showForm\n = !showProcessing\n && (c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing');\n const failedMessage = getLearnerFacingCheckoutMessage(c.lastComment);\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const isBillingCountryBlocked = GATE_CHECK_COUNTRY\n && !!c.billing.country\n && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);\n\n const selectedCycle: BillingCycle = c.selectedCycle;\n\n // Prefer the à la carte course's own detail (skills/badge/image, richer than\n // the subscription flow's ?course_run_key= lookup) when we selected a course.\n const alacarteCourseDetail: CourseDetail | null\n = alacarte.line?.kind === 'course' && alacarte.productDetail && 'maId' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n const courseForSummary = alacarteCourseDetail ?? c.course;\n\n const alacarteProgramDetail: ProgramDetail | null\n = alacarte.line?.kind === 'program' && alacarte.productDetail && 'tCourses' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n\n const handleStartCourse = () => {\n const alacarteCourseUrl = alacarteCourseDetail?.tUrlCourse\n || (alacarte.productDetail && 'tUrlCourse' in alacarte.productDetail\n ? alacarte.productDetail.tUrlCourse\n : null);\n if (alacarteCourseUrl) {\n window.location.href = alacarteCourseUrl;\n } else if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = homeUrl || '/';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n if (showProcessing) {\n return <ProcessingScreen />;\n }\n\n // TEMPORARY — flip to true to dump à la carte hook state while validating\n // against the real backend. Remove AlacarteDebugPanel once the flow is solid.\n const SHOW_ALACARTE_DEBUG = false;\n const alacarteDebug = SHOW_ALACARTE_DEBUG\n ? <AlacarteDebugPanel data={alacarte} />\n : null;\n\n // Confirmation: show once the order is ready (or failed to load). While\n // tPlacedOrder is still polling, keep the checkout form so Pay now can show\n // \"Processing...\" instead of a full-page loader.\n if (alacarte.placedOrder || alacarte.placedOrderError) {\n const confirmationTitle = alacarte.line?.kind === 'program'\n ? alacarte.line.programName\n : alacarte.line?.kind === 'course'\n ? alacarte.line.courseName\n : null;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <AlacarteConfirmation\n order={alacarte.placedOrder}\n awaiting={false}\n error={alacarte.placedOrderError}\n onContinue={handleStartCourse}\n productDetail={alacarte.productDetail}\n productTitle={confirmationTitle}\n orderHistoryUrl={orderHistoryUrl ?? null}\n />\n </Col>\n </Row>\n </>\n );\n }\n\n if (alacarte.phase === 'loading') {\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout' && REDIRECT_ON_NO_ACTIVE_CHECKOUT) {\n // Redirects home via the effect above — show a spinner rather than a\n // notice while that happens.\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'alreadyOwned' || alacarte.phase === 'noActiveCheckout') {\n return (\n <>\n {alacarteDebug}\n <AlacarteNotice messageId={alacarte.noticeMessageId!} />\n </>\n );\n }\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <CountryUnavailableModal\n isOpen={isBillingCountryBlocked}\n homeUrl={homeUrl}\n />\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n <Col xs={12}>\n <Row className=\"flex-column-reverse gap-5 flex-lg-row\">\n {/* ── LEFT ── */}\n <Col xs={12} lg={7} xxl={9} className=\"payment__options\">\n <div>\n <h1 className=\"font-weight-bold text-primary-900 d-none d-lg-block mb-4\">\n Checkout\n </h1>\n {c.error && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={c.clearError}\n >\n {c.error}\n </Alert>\n )}\n\n {alacarte.placeOrderError && (\n <Alert\n variant=\"warning\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearPlaceOrderError}\n className=\"mb-3\"\n >\n {alacarte.placeOrderError}\n </Alert>\n )}\n\n {!alacarte.priceAccepted && isAlacarteReady && alacarte.line?.amountDue && (\n <Alert variant=\"info\" className=\"mb-3\" data-testid=\"price-accept-notice\">\n {formatMessage(messages.priceChangedNotice, {\n amount: alacarte.line.amountDue.amount.toFixed(2),\n currency: alacarte.line.amountDue.currency,\n })}\n </Alert>\n )}\n\n {showForm && (\n <>\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || formatMessage(messages.paymentProcessingDescription)}\n </Alert>\n )}\n {appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={() => setDismissedCouponAlert(appliedCouponCode)}\n className=\"coupon-applied-alert\"\n data-testid=\"coupon-applied-alert\"\n >\n <span aria-hidden=\"true\">🎉 </span>\n {formatMessage(messages.couponAppliedAlert, {\n code: appliedCouponCode,\n percent: appliedDiscount.percent,\n })}\n </Alert>\n )}\n <PaymentForm\n billing={c.billing}\n setField={c.setField}\n numberMountRef={c.numberMountRef}\n expiryMountRef={c.expiryMountRef}\n cvcMountRef={c.cvcMountRef}\n cardError={c.cardError}\n stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={\n isAlacarteReady\n ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)\n : c.processingSubscription\n }\n onPlaceOrder={\n isAlacarteReady\n ? () => {\n alacarte.placeOrder({\n billing: {\n ...c.billing,\n // Country is required on tCheckoutPlaceOrder (two-letter ISO).\n // Prefer the form selection; fall back to gate geolocation.\n country: c.billing.country || c.userCountry || '',\n },\n confirmCardPayment: c.confirmCardPayment,\n });\n }\n : c.placeOrder\n }\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={null}\n cycleToShow={selectedCycle === 'monthly' ? 'monthly' : 'annual'}\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n alacarteLine={alacarte.line}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n courseLabel={courseLabel}\n onFindCourse={handleStartCourse}\n />\n )}\n\n {c.phase === 'blocked' && (\n <BlockedScreen detail={c.lastComment} />\n )}\n </div>\n </Col>\n\n {/* ── RIGHT — swaps to EnrollmentSummary on success ── */}\n <Col xs={12} lg={5} xxl={3} className=\"payment__summary\">\n {c.phase === 'success' ? (\n <EnrollmentSummary\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={courseForSummary}\n alacarteLine={alacarte.line}\n programDetail={alacarteProgramDetail}\n promoCode={isAlacarteReady ? {\n value: promoCodeValue,\n onChange: (next) => {\n setPromoCodeValue(next);\n if (alacarte.couponError) alacarte.clearCouponError();\n },\n onApply: () => alacarte.applyCoupon(promoCodeValue),\n onRemove: () => {\n alacarte.removeCoupon();\n setPromoCodeValue('');\n setAdvertisedOffer(null);\n },\n appliedCode: appliedCouponCode,\n discount: appliedDiscount,\n offer: !appliedCouponCode ? advertisedOffer : null,\n error: alacarte.couponError,\n isApplying: alacarte.couponApplying,\n disabled: alacarte.placingOrder,\n } : null}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n </>\n );\n};\n\nexport default Checkout;\n"]}
|
|
1
|
+
{"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useState } from 'react';\nimport { Alert, Col, Row } from '@openedx/paragon';\nimport { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';\nimport { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport AlacarteNotice from '../AlacarteNotice/AlacarteNotice';\nimport AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';\nimport AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel\nimport { getTrialDateRange } from '@src/utils';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { appliedDiscountPercent } from '../../utils/discount';\nimport { confirmPaymentWithMethodId } from '../../utils/stripeCard';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle, CourseDetail } from '../../types';\nimport type { ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\n// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a\n// banner instead so the case is visible while the flow is being verified.\n// Flip back to true to restore the redirect.\nconst REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** Endpoint used to look up the representative IP for a selected country. */\n geolocationUrl?: string,\n /** Learner dashboard / order history URL for confirmation CTAs. */\n orderHistoryUrl?: string,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n orderHistoryUrl,\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });\n const c = useCheckout(props);\n const [promoCodeValue, setPromoCodeValue] = useState('');\n const [dismissedCouponAlert, setDismissedCouponAlert] = useState<string | null>(null);\n // Last code we successfully priced — drives the \"Save {percent}% with code\n // {code}\" hint on the empty field while a coupon is not yet applied.\n const [advertisedOffer, setAdvertisedOffer] = useState<{ code: string, percent: number } | null>(null);\n const isAlacarteReady = alacarte.phase === 'ready' && !!alacarte.checkout;\n const appliedCouponCode = alacarte.checkout?.couponCode?.trim()\n ? alacarte.checkout.couponCode.trim()\n : null;\n const discountPercent = appliedDiscountPercent(alacarte.line);\n const appliedDiscount = appliedCouponCode && alacarte.line?.discount && discountPercent != null\n ? {\n percent: discountPercent,\n amount: alacarte.line.discount.amount,\n currency: alacarte.line.discount.currency,\n }\n : null;\n\n // Keep the coupon field in sync when a code is applied via URL or API.\n useEffect(() => {\n if (alacarte.checkout?.couponCode) {\n setPromoCodeValue(alacarte.checkout.couponCode);\n }\n }, [alacarte.checkout?.couponCode]);\n\n useEffect(() => {\n if (appliedCouponCode && discountPercent != null) {\n setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });\n }\n }, [appliedCouponCode, discountPercent]);\n\n // No query params and no active cart — nothing to check out, send the\n // learner home rather than showing a placeholder.\n useEffect(() => {\n if (REDIRECT_ON_NO_ACTIVE_CHECKOUT && alacarte.phase === 'noActiveCheckout') {\n window.location.href = homeUrl;\n }\n }, [alacarte.phase, homeUrl]);\n\n // Express checkout (Apple Pay / Google Pay): runs the full place-order flow\n // using a wallet-provided PaymentMethod id instead of the mounted card element.\n const handleExpressPayment = useCallback(async (paymentMethodId: string, walletCountry?: string) => {\n if (!c.stripe) throw new Error('Stripe not loaded');\n const stripeInstance = c.stripe;\n\n await alacarte.placeOrderAsync({\n billing: {\n ...c.billing,\n country: c.billing.country || walletCountry || c.userCountry || '',\n },\n confirmCardPayment: async (clientSecret: string) => {\n await confirmPaymentWithMethodId({\n stripe: stripeInstance,\n clientSecret,\n paymentMethodId,\n });\n },\n });\n }, [alacarte, c.billing, c.userCountry, c.stripe]);\n\n const showProcessing\n = c.returningFromStripe\n && c.processingSubscription\n && c.phase !== 'success'\n && c.phase !== 'blocked'\n && c.phase !== 'failed';\n const showForm\n = !showProcessing\n && (c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing');\n const failedMessage = getLearnerFacingCheckoutMessage(c.lastComment);\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const isBillingCountryBlocked = GATE_CHECK_COUNTRY\n && !!c.billing.country\n && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);\n\n const selectedCycle: BillingCycle = c.selectedCycle;\n\n // Prefer the à la carte course's own detail (skills/badge/image, richer than\n // the subscription flow's ?course_run_key= lookup) when we selected a course.\n const alacarteCourseDetail: CourseDetail | null\n = alacarte.line?.kind === 'course' && alacarte.productDetail && 'maId' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n const courseForSummary = alacarteCourseDetail ?? c.course;\n\n const alacarteProgramDetail: ProgramDetail | null\n = alacarte.line?.kind === 'program' && alacarte.productDetail && 'tCourses' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n\n const handleStartCourse = () => {\n const alacarteCourseUrl = alacarteCourseDetail?.tUrlCourse\n || (alacarte.productDetail && 'tUrlCourse' in alacarte.productDetail\n ? alacarte.productDetail.tUrlCourse\n : null);\n if (alacarteCourseUrl) {\n window.location.href = alacarteCourseUrl;\n } else if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = homeUrl || '/';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n if (showProcessing) {\n return <ProcessingScreen />;\n }\n\n // TEMPORARY — flip to true to dump à la carte hook state while validating\n // against the real backend. Remove AlacarteDebugPanel once the flow is solid.\n const SHOW_ALACARTE_DEBUG = false;\n const alacarteDebug = SHOW_ALACARTE_DEBUG\n ? <AlacarteDebugPanel data={alacarte} />\n : null;\n\n // Confirmation: show once the order is ready (or failed to load). While\n // tPlacedOrder is still polling, keep the checkout form so Pay now can show\n // \"Processing...\" instead of a full-page loader.\n if (alacarte.placedOrder || alacarte.placedOrderError) {\n const confirmationTitle = alacarte.line?.kind === 'program'\n ? alacarte.line.programName\n : alacarte.line?.kind === 'course'\n ? alacarte.line.courseName\n : null;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <AlacarteConfirmation\n order={alacarte.placedOrder}\n awaiting={false}\n error={alacarte.placedOrderError}\n onContinue={handleStartCourse}\n productDetail={alacarte.productDetail}\n productTitle={confirmationTitle}\n orderHistoryUrl={orderHistoryUrl ?? null}\n />\n </Col>\n </Row>\n </>\n );\n }\n\n if (alacarte.phase === 'loading') {\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout' && REDIRECT_ON_NO_ACTIVE_CHECKOUT) {\n // Redirects home via the effect above — show a spinner rather than a\n // notice while that happens.\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'alreadyOwned' || alacarte.phase === 'noActiveCheckout') {\n return (\n <>\n {alacarteDebug}\n <AlacarteNotice messageId={alacarte.noticeMessageId!} />\n </>\n );\n }\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <CountryUnavailableModal\n isOpen={isBillingCountryBlocked}\n homeUrl={homeUrl}\n />\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n <Col xs={12}>\n <Row className=\"flex-column-reverse gap-5 flex-lg-row\">\n {/* ── LEFT ── */}\n <Col xs={12} lg={7} xxl={9} className=\"payment__options\">\n <div>\n <h1 className=\"font-weight-bold text-primary-900 d-none d-lg-block mb-4\">\n Checkout\n </h1>\n {c.error && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={c.clearError}\n >\n {c.error}\n </Alert>\n )}\n\n {alacarte.placeOrderError && (\n <Alert\n variant=\"warning\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearPlaceOrderError}\n className=\"mb-3\"\n >\n {alacarte.placeOrderError}\n </Alert>\n )}\n\n {!alacarte.priceAccepted && isAlacarteReady && alacarte.line?.amountDue && (\n <Alert variant=\"info\" className=\"mb-3\" data-testid=\"price-accept-notice\">\n {formatMessage(messages.priceChangedNotice, {\n amount: alacarte.line.amountDue.amount.toFixed(2),\n currency: alacarte.line.amountDue.currency,\n })}\n </Alert>\n )}\n\n {showForm && (\n <>\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || formatMessage(messages.paymentProcessingDescription)}\n </Alert>\n )}\n {appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={() => setDismissedCouponAlert(appliedCouponCode)}\n className=\"coupon-applied-alert\"\n data-testid=\"coupon-applied-alert\"\n >\n <span aria-hidden=\"true\">🎉 </span>\n {formatMessage(messages.couponAppliedAlert, {\n code: appliedCouponCode,\n percent: appliedDiscount.percent,\n })}\n </Alert>\n )}\n <PaymentForm\n billing={c.billing}\n setField={c.setField}\n numberMountRef={c.numberMountRef}\n expiryMountRef={c.expiryMountRef}\n cvcMountRef={c.cvcMountRef}\n cardError={c.cardError}\n stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={\n isAlacarteReady\n ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)\n : c.processingSubscription\n }\n onPlaceOrder={\n isAlacarteReady\n ? () => {\n alacarte.placeOrder({\n billing: {\n ...c.billing,\n // Country is required on tCheckoutPlaceOrder (two-letter ISO).\n // Prefer the form selection; fall back to gate geolocation.\n country: c.billing.country || c.userCountry || '',\n },\n confirmCardPayment: c.confirmCardPayment,\n });\n }\n : c.placeOrder\n }\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={null}\n cycleToShow={selectedCycle === 'monthly' ? 'monthly' : 'annual'}\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n alacarteLine={alacarte.line}\n stripe={c.stripe}\n expressCountry={c.billing.country || c.userCountry || 'US'}\n onExpressPayment={isAlacarteReady ? handleExpressPayment : undefined}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n courseLabel={courseLabel}\n onFindCourse={handleStartCourse}\n />\n )}\n\n {c.phase === 'blocked' && (\n <BlockedScreen detail={c.lastComment} />\n )}\n </div>\n </Col>\n\n {/* ── RIGHT — swaps to EnrollmentSummary on success ── */}\n <Col xs={12} lg={5} xxl={3} className=\"payment__summary\">\n {c.phase === 'success' ? (\n <EnrollmentSummary\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={courseForSummary}\n alacarteLine={alacarte.line}\n programDetail={alacarteProgramDetail}\n promoCode={isAlacarteReady ? {\n value: promoCodeValue,\n onChange: (next) => {\n setPromoCodeValue(next);\n if (alacarte.couponError) alacarte.clearCouponError();\n },\n onApply: () => alacarte.applyCoupon(promoCodeValue),\n onRemove: () => {\n alacarte.removeCoupon();\n setPromoCodeValue('');\n setAdvertisedOffer(null);\n },\n appliedCode: appliedCouponCode,\n discount: appliedDiscount,\n offer: !appliedCouponCode ? advertisedOffer : null,\n error: alacarte.couponError,\n isApplying: alacarte.couponApplying,\n disabled: alacarte.placingOrder,\n } : null}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n </>\n );\n};\n\nexport default Checkout;\n"]}
|
|
@@ -1,2 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { StripeLike } from '../../types';
|
|
3
|
+
export interface ExpressCheckoutProps {
|
|
4
|
+
/** Stripe.js instance — null until loaded. */
|
|
5
|
+
stripe: StripeLike | null;
|
|
6
|
+
/** Total amount in the smallest currency unit (cents). */
|
|
7
|
+
totalAmountCents: number | null;
|
|
8
|
+
/** ISO 4217 currency code, e.g. 'USD'. */
|
|
9
|
+
currency: string | null;
|
|
10
|
+
/** Two-letter country code for Payment Request initialisation. */
|
|
11
|
+
country: string;
|
|
12
|
+
/**
|
|
13
|
+
* Called when the user authorises a wallet payment (Apple Pay / Google Pay).
|
|
14
|
+
* Receives the Stripe PaymentMethod id and the billing country from the wallet.
|
|
15
|
+
* Must resolve on success or reject on failure.
|
|
16
|
+
*/
|
|
17
|
+
onPaymentAuthorized: (paymentMethodId: string, walletCountry?: string) => Promise<void>;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
}
|
|
20
|
+
declare const ExpressCheckout: React.FC<ExpressCheckoutProps>;
|
|
2
21
|
export default ExpressCheckout;
|
|
@@ -1,12 +1,87 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
12
|
import { useIntl } from '@openedx/frontend-base';
|
|
3
13
|
import { PayPalWordmark } from '../BrandMarks/BrandMarks';
|
|
4
14
|
import messages from './Messages';
|
|
5
15
|
const AppleLogo = () => (_jsxs("svg", { width: "16", height: "19", viewBox: "0 0 16 19", fill: "none", "aria-hidden": "true", children: [_jsx("path", { d: "M13.05 10.1c-.02-2.09 1.71-3.09 1.79-3.14-.98-1.43-2.5-1.63-3.04-1.65-1.29-.13-2.53.76-3.19.76-.66 0-1.68-.74-2.76-.72-1.4.02-2.71.82-3.43 2.06-1.48 2.56-.38 6.36 1.06 8.44.7 1.02 1.54 2.16 2.63 2.12 1.06-.04 1.46-.68 2.74-.68 1.28 0 1.64.68 2.76.66 1.14-.02 1.86-1.04 2.56-2.06.8-1.19 1.13-2.34 1.15-2.4-.03-.01-2.2-.84-2.27-3.39z", fill: "#fff" }), _jsx("path", { d: "M11.03 3.86c.58-.71.98-1.68.87-2.66-.84.03-1.87.56-2.47 1.26-.54.62-1.02 1.63-.89 2.58.94.07 1.9-.48 2.49-1.18z", fill: "#fff" })] }));
|
|
6
16
|
const GooglePayIcon = () => (_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", "aria-hidden": "true", children: [_jsx("path", { d: "M10.2 8.2v3.32h4.6c-.2 1.06-.8 1.96-1.7 2.56v2.13h2.75c1.6-1.48 2.53-3.66 2.53-6.25 0-.6-.05-1.18-.15-1.75l-8.03-.01z", fill: "#4285F4" }), _jsx("path", { d: "M10.2 18c2.3 0 4.23-.76 5.65-2.06l-2.75-2.13c-.76.51-1.74.82-2.9.82-2.23 0-4.12-1.5-4.79-3.53H2.58v2.2A9.2 9.2 0 0010.2 18z", fill: "#34A853" }), _jsx("path", { d: "M5.41 11.1a5.53 5.53 0 010-3.53v-2.2H2.58a9.2 9.2 0 000 7.93l2.83-2.2z", fill: "#FBBC05" }), _jsx("path", { d: "M10.2 4.04c1.25 0 2.38.43 3.26 1.28l2.44-2.44A9.09 9.09 0 0010.2 0a9.2 9.2 0 00-7.62 4.6l2.83 2.2c.67-2.02 2.56-2.76 4.79-2.76z", fill: "#EA4335" })] }));
|
|
7
|
-
const ExpressCheckout = () => {
|
|
17
|
+
const ExpressCheckout = ({ stripe, totalAmountCents, currency, country, onPaymentAuthorized, disabled, }) => {
|
|
8
18
|
const { formatMessage } = useIntl();
|
|
9
|
-
|
|
19
|
+
const paymentRequestRef = useRef(null);
|
|
20
|
+
const onPaymentAuthorizedRef = useRef(onPaymentAuthorized);
|
|
21
|
+
const [paymentReady, setPaymentReady] = useState(false);
|
|
22
|
+
// Keep callback ref current without recreating the payment request.
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
onPaymentAuthorizedRef.current = onPaymentAuthorized;
|
|
25
|
+
}, [onPaymentAuthorized]);
|
|
26
|
+
// Create the Stripe PaymentRequest and check availability.
|
|
27
|
+
// We use our own custom-styled buttons (not Stripe's paymentRequestButton)
|
|
28
|
+
// so we always show Apple Pay / Google Pay branding — never the Link button.
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!stripe || !totalAmountCents || !currency) {
|
|
31
|
+
setPaymentReady(false);
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const pr = stripe.paymentRequest({
|
|
35
|
+
country: country || 'US',
|
|
36
|
+
currency: currency.toLowerCase(),
|
|
37
|
+
total: { label: 'edX', amount: totalAmountCents },
|
|
38
|
+
requestPayerName: true,
|
|
39
|
+
requestPayerEmail: true,
|
|
40
|
+
});
|
|
41
|
+
paymentRequestRef.current = pr;
|
|
42
|
+
pr.on('paymentmethod', (ev) => __awaiter(void 0, void 0, void 0, function* () {
|
|
43
|
+
var _a, _b;
|
|
44
|
+
try {
|
|
45
|
+
const walletCountry = (_b = (_a = ev.paymentMethod.billing_details) === null || _a === void 0 ? void 0 : _a.address) === null || _b === void 0 ? void 0 : _b.country;
|
|
46
|
+
yield onPaymentAuthorizedRef.current(ev.paymentMethod.id, walletCountry);
|
|
47
|
+
ev.complete('success');
|
|
48
|
+
}
|
|
49
|
+
catch (_c) {
|
|
50
|
+
ev.complete('fail');
|
|
51
|
+
}
|
|
52
|
+
}));
|
|
53
|
+
let cancelled = false;
|
|
54
|
+
pr.canMakePayment().then((result) => {
|
|
55
|
+
if (cancelled)
|
|
56
|
+
return;
|
|
57
|
+
console.log('[ExpressCheckout] canMakePayment result:', JSON.stringify(result, null, 2));
|
|
58
|
+
setPaymentReady(result !== null);
|
|
59
|
+
});
|
|
60
|
+
return () => {
|
|
61
|
+
cancelled = true;
|
|
62
|
+
paymentRequestRef.current = null;
|
|
63
|
+
};
|
|
64
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
65
|
+
}, [stripe, currency, country]);
|
|
66
|
+
// Update the payment request total when the amount changes (e.g. coupon applied).
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (!paymentRequestRef.current || !totalAmountCents)
|
|
69
|
+
return;
|
|
70
|
+
try {
|
|
71
|
+
paymentRequestRef.current.update({
|
|
72
|
+
total: { label: 'edX', amount: totalAmountCents },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (_a) {
|
|
76
|
+
// update() may throw if the sheet is already open — safe to ignore.
|
|
77
|
+
}
|
|
78
|
+
}, [totalAmountCents]);
|
|
79
|
+
const handleWalletClick = () => {
|
|
80
|
+
var _a;
|
|
81
|
+
(_a = paymentRequestRef.current) === null || _a === void 0 ? void 0 : _a.show();
|
|
82
|
+
};
|
|
83
|
+
const buttonsDisabled = disabled || !paymentReady;
|
|
84
|
+
return (_jsxs("div", { className: "express-checkout", children: [_jsx("h3", { className: "express-checkout__heading", children: formatMessage(messages.heading) }), _jsxs("div", { className: "express-checkout__buttons", children: [_jsx("button", { type: "button", disabled: true, "aria-label": formatMessage(messages.paypal), className: "express-checkout__button express-checkout__button--paypal", children: _jsx(PayPalWordmark, {}) }), _jsxs("button", { type: "button", disabled: buttonsDisabled, onClick: handleWalletClick, "aria-label": formatMessage(messages.applePay), className: "express-checkout__button express-checkout__button--apple-pay", children: [_jsx(AppleLogo, {}), "Pay"] }), _jsxs("button", { type: "button", disabled: buttonsDisabled, onClick: handleWalletClick, "aria-label": formatMessage(messages.googlePay), className: "express-checkout__button express-checkout__button--google-pay", children: [_jsx(GooglePayIcon, {}), "Pay"] })] })] }));
|
|
10
85
|
};
|
|
11
86
|
export default ExpressCheckout;
|
|
12
87
|
//# sourceMappingURL=ExpressCheckout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpressCheckout.js","sourceRoot":"","sources":["../../../src/components/ExpressCheckout/ExpressCheckout.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,CACtB,eAAK,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,iBAAa,MAAM,aAC5E,eACE,CAAC,EAAC,6UAA6U,EAC/U,IAAI,EAAC,MAAM,GACX,EACF,eACE,CAAC,EAAC,iHAAiH,EACnH,IAAI,EAAC,MAAM,GACX,IACE,CACP,CAAC;AAEF,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,CAC1B,eAAK,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,iBAAa,MAAM,aAChE,eACE,CAAC,EAAC,uHAAuH,EACzH,IAAI,EAAC,SAAS,GACd,EACF,eACE,CAAC,EAAC,6HAA6H,EAC/H,IAAI,EAAC,SAAS,GACd,EACF,eACE,CAAC,EAAC,wEAAwE,EAC1E,IAAI,EAAC,SAAS,GACd,EACF,eACE,CAAC,EAAC,iIAAiI,EACnI,IAAI,EAAC,SAAS,GACd,IACE,CACP,CAAC;AAEF,MAAM,eAAe,GAAG,GAAG,EAAE;IAC3B,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IAEpC,OAAO,CACL,eAAK,SAAS,EAAC,kBAAkB,aAC/B,aAAI,SAAS,EAAC,2BAA2B,YACtC,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,GAC7B,EACL,eAAK,SAAS,EAAC,2BAA2B,aACxC,iBACE,IAAI,EAAC,QAAQ,EACb,QAAQ,sBACI,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,EAC1C,SAAS,EAAC,2DAA2D,YAErE,KAAC,cAAc,KAAG,GACX,EACT,kBACE,IAAI,EAAC,QAAQ,EACb,QAAQ,sBACI,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAC5C,SAAS,EAAC,8DAA8D,aAExE,KAAC,SAAS,KAAG,WAEN,EACT,kBACE,IAAI,EAAC,QAAQ,EACb,QAAQ,sBACI,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,EAC7C,SAAS,EAAC,+DAA+D,aAEzE,KAAC,aAAa,KAAG,WAEV,IACL,IACF,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,eAAe,CAAC","sourcesContent":["import React from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport { PayPalWordmark } from '../BrandMarks/BrandMarks';\nimport messages from './Messages';\n\nconst AppleLogo = () => (\n <svg width=\"16\" height=\"19\" viewBox=\"0 0 16 19\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M13.05 10.1c-.02-2.09 1.71-3.09 1.79-3.14-.98-1.43-2.5-1.63-3.04-1.65-1.29-.13-2.53.76-3.19.76-.66 0-1.68-.74-2.76-.72-1.4.02-2.71.82-3.43 2.06-1.48 2.56-.38 6.36 1.06 8.44.7 1.02 1.54 2.16 2.63 2.12 1.06-.04 1.46-.68 2.74-.68 1.28 0 1.64.68 2.76.66 1.14-.02 1.86-1.04 2.56-2.06.8-1.19 1.13-2.34 1.15-2.4-.03-.01-2.2-.84-2.27-3.39z\"\n fill=\"#fff\"\n />\n <path\n d=\"M11.03 3.86c.58-.71.98-1.68.87-2.66-.84.03-1.87.56-2.47 1.26-.54.62-1.02 1.63-.89 2.58.94.07 1.9-.48 2.49-1.18z\"\n fill=\"#fff\"\n />\n </svg>\n);\n\nconst GooglePayIcon = () => (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10.2 8.2v3.32h4.6c-.2 1.06-.8 1.96-1.7 2.56v2.13h2.75c1.6-1.48 2.53-3.66 2.53-6.25 0-.6-.05-1.18-.15-1.75l-8.03-.01z\"\n fill=\"#4285F4\"\n />\n <path\n d=\"M10.2 18c2.3 0 4.23-.76 5.65-2.06l-2.75-2.13c-.76.51-1.74.82-2.9.82-2.23 0-4.12-1.5-4.79-3.53H2.58v2.2A9.2 9.2 0 0010.2 18z\"\n fill=\"#34A853\"\n />\n <path\n d=\"M5.41 11.1a5.53 5.53 0 010-3.53v-2.2H2.58a9.2 9.2 0 000 7.93l2.83-2.2z\"\n fill=\"#FBBC05\"\n />\n <path\n d=\"M10.2 4.04c1.25 0 2.38.43 3.26 1.28l2.44-2.44A9.09 9.09 0 0010.2 0a9.2 9.2 0 00-7.62 4.6l2.83 2.2c.67-2.02 2.56-2.76 4.79-2.76z\"\n fill=\"#EA4335\"\n />\n </svg>\n);\n\nconst ExpressCheckout = () => {\n const { formatMessage } = useIntl();\n\n return (\n <div className=\"express-checkout\">\n <h3 className=\"express-checkout__heading\">\n {formatMessage(messages.heading)}\n </h3>\n <div className=\"express-checkout__buttons\">\n <button\n type=\"button\"\n disabled\n aria-label={formatMessage(messages.paypal)}\n className=\"express-checkout__button express-checkout__button--paypal\"\n >\n <PayPalWordmark />\n </button>\n <button\n type=\"button\"\n disabled\n aria-label={formatMessage(messages.applePay)}\n className=\"express-checkout__button express-checkout__button--apple-pay\"\n >\n <AppleLogo />\n Pay\n </button>\n <button\n type=\"button\"\n disabled\n aria-label={formatMessage(messages.googlePay)}\n className=\"express-checkout__button express-checkout__button--google-pay\"\n >\n <GooglePayIcon />\n Pay\n </button>\n </div>\n </div>\n );\n};\n\nexport default ExpressCheckout;\n"]}
|
|
1
|
+
{"version":3,"file":"ExpressCheckout.js","sourceRoot":"","sources":["../../../src/components/ExpressCheckout/ExpressCheckout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport type { StripeLike, PaymentRequestLike } from '../../types';\nimport { PayPalWordmark } from '../BrandMarks/BrandMarks';\nimport messages from './Messages';\n\nconst AppleLogo = () => (\n <svg width=\"16\" height=\"19\" viewBox=\"0 0 16 19\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M13.05 10.1c-.02-2.09 1.71-3.09 1.79-3.14-.98-1.43-2.5-1.63-3.04-1.65-1.29-.13-2.53.76-3.19.76-.66 0-1.68-.74-2.76-.72-1.4.02-2.71.82-3.43 2.06-1.48 2.56-.38 6.36 1.06 8.44.7 1.02 1.54 2.16 2.63 2.12 1.06-.04 1.46-.68 2.74-.68 1.28 0 1.64.68 2.76.66 1.14-.02 1.86-1.04 2.56-2.06.8-1.19 1.13-2.34 1.15-2.4-.03-.01-2.2-.84-2.27-3.39z\"\n fill=\"#fff\"\n />\n <path\n d=\"M11.03 3.86c.58-.71.98-1.68.87-2.66-.84.03-1.87.56-2.47 1.26-.54.62-1.02 1.63-.89 2.58.94.07 1.9-.48 2.49-1.18z\"\n fill=\"#fff\"\n />\n </svg>\n);\n\nconst GooglePayIcon = () => (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10.2 8.2v3.32h4.6c-.2 1.06-.8 1.96-1.7 2.56v2.13h2.75c1.6-1.48 2.53-3.66 2.53-6.25 0-.6-.05-1.18-.15-1.75l-8.03-.01z\"\n fill=\"#4285F4\"\n />\n <path\n d=\"M10.2 18c2.3 0 4.23-.76 5.65-2.06l-2.75-2.13c-.76.51-1.74.82-2.9.82-2.23 0-4.12-1.5-4.79-3.53H2.58v2.2A9.2 9.2 0 0010.2 18z\"\n fill=\"#34A853\"\n />\n <path\n d=\"M5.41 11.1a5.53 5.53 0 010-3.53v-2.2H2.58a9.2 9.2 0 000 7.93l2.83-2.2z\"\n fill=\"#FBBC05\"\n />\n <path\n d=\"M10.2 4.04c1.25 0 2.38.43 3.26 1.28l2.44-2.44A9.09 9.09 0 0010.2 0a9.2 9.2 0 00-7.62 4.6l2.83 2.2c.67-2.02 2.56-2.76 4.79-2.76z\"\n fill=\"#EA4335\"\n />\n </svg>\n);\n\nexport interface ExpressCheckoutProps {\n /** Stripe.js instance — null until loaded. */\n stripe: StripeLike | null,\n /** Total amount in the smallest currency unit (cents). */\n totalAmountCents: number | null,\n /** ISO 4217 currency code, e.g. 'USD'. */\n currency: string | null,\n /** Two-letter country code for Payment Request initialisation. */\n country: string,\n /**\n * Called when the user authorises a wallet payment (Apple Pay / Google Pay).\n * Receives the Stripe PaymentMethod id and the billing country from the wallet.\n * Must resolve on success or reject on failure.\n */\n onPaymentAuthorized: (paymentMethodId: string, walletCountry?: string) => Promise<void>,\n disabled?: boolean,\n}\n\nconst ExpressCheckout: React.FC<ExpressCheckoutProps> = ({\n stripe,\n totalAmountCents,\n currency,\n country,\n onPaymentAuthorized,\n disabled,\n}) => {\n const { formatMessage } = useIntl();\n const paymentRequestRef = useRef<PaymentRequestLike | null>(null);\n const onPaymentAuthorizedRef = useRef(onPaymentAuthorized);\n const [paymentReady, setPaymentReady] = useState(false);\n\n // Keep callback ref current without recreating the payment request.\n useEffect(() => {\n onPaymentAuthorizedRef.current = onPaymentAuthorized;\n }, [onPaymentAuthorized]);\n\n // Create the Stripe PaymentRequest and check availability.\n // We use our own custom-styled buttons (not Stripe's paymentRequestButton)\n // so we always show Apple Pay / Google Pay branding — never the Link button.\n useEffect(() => {\n if (!stripe || !totalAmountCents || !currency) {\n setPaymentReady(false);\n return undefined;\n }\n\n const pr = stripe.paymentRequest({\n country: country || 'US',\n currency: currency.toLowerCase(),\n total: { label: 'edX', amount: totalAmountCents },\n requestPayerName: true,\n requestPayerEmail: true,\n });\n\n paymentRequestRef.current = pr;\n\n pr.on('paymentmethod', async (ev) => {\n try {\n const walletCountry = ev.paymentMethod.billing_details?.address?.country;\n await onPaymentAuthorizedRef.current(ev.paymentMethod.id, walletCountry);\n ev.complete('success');\n } catch {\n ev.complete('fail');\n }\n });\n\n let cancelled = false;\n\n pr.canMakePayment().then((result) => {\n if (cancelled) return;\n console.log('[ExpressCheckout] canMakePayment result:', JSON.stringify(result, null, 2));\n setPaymentReady(result !== null);\n });\n\n return () => {\n cancelled = true;\n paymentRequestRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [stripe, currency, country]);\n\n // Update the payment request total when the amount changes (e.g. coupon applied).\n useEffect(() => {\n if (!paymentRequestRef.current || !totalAmountCents) return;\n try {\n paymentRequestRef.current.update({\n total: { label: 'edX', amount: totalAmountCents },\n });\n } catch {\n // update() may throw if the sheet is already open — safe to ignore.\n }\n }, [totalAmountCents]);\n\n const handleWalletClick = () => {\n paymentRequestRef.current?.show();\n };\n\n const buttonsDisabled = disabled || !paymentReady;\n\n return (\n <div className=\"express-checkout\">\n <h3 className=\"express-checkout__heading\">\n {formatMessage(messages.heading)}\n </h3>\n <div className=\"express-checkout__buttons\">\n <button\n type=\"button\"\n disabled\n aria-label={formatMessage(messages.paypal)}\n className=\"express-checkout__button express-checkout__button--paypal\"\n >\n <PayPalWordmark />\n </button>\n <button\n type=\"button\"\n disabled={buttonsDisabled}\n onClick={handleWalletClick}\n aria-label={formatMessage(messages.applePay)}\n className=\"express-checkout__button express-checkout__button--apple-pay\"\n >\n <AppleLogo />\n Pay\n </button>\n <button\n type=\"button\"\n disabled={buttonsDisabled}\n onClick={handleWalletClick}\n aria-label={formatMessage(messages.googlePay)}\n className=\"express-checkout__button express-checkout__button--google-pay\"\n >\n <GooglePayIcon />\n Pay\n </button>\n </div>\n </div>\n );\n};\n\nexport default ExpressCheckout;\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Ref } from 'react';
|
|
2
|
-
import type { BillingInfo, CheckoutConsent, SupportedPaymentMethod } from '../../types';
|
|
2
|
+
import type { BillingInfo, CheckoutConsent, StripeLike, SupportedPaymentMethod } from '../../types';
|
|
3
3
|
import type { AlacarteLine } from '../../types/alacarte';
|
|
4
4
|
export interface PaymentFormProps {
|
|
5
5
|
billing: BillingInfo;
|
|
@@ -29,6 +29,12 @@ export interface PaymentFormProps {
|
|
|
29
29
|
geolocationUrl?: string;
|
|
30
30
|
/** À la carte checkout line — when present, drives the order total shown. */
|
|
31
31
|
alacarteLine?: AlacarteLine | null;
|
|
32
|
+
/** Stripe.js instance for express checkout (Apple Pay / Google Pay). */
|
|
33
|
+
stripe?: StripeLike | null;
|
|
34
|
+
/** Two-letter country code for express checkout initialisation. */
|
|
35
|
+
expressCountry?: string;
|
|
36
|
+
/** Called when the user authorises a wallet payment. */
|
|
37
|
+
onExpressPayment?: (paymentMethodId: string, walletCountry?: string) => Promise<void>;
|
|
32
38
|
}
|
|
33
|
-
declare const PaymentForm: ({ billing, setField, numberMountRef, expiryMountRef, cvcMountRef, stripeReady, sessionReady, submitting, onPlaceOrder, alacarteLine, }: PaymentFormProps) => import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
declare const PaymentForm: ({ billing, setField, numberMountRef, expiryMountRef, cvcMountRef, stripeReady, sessionReady, submitting, onPlaceOrder, alacarteLine, stripe, expressCountry, onExpressPayment, }: PaymentFormProps) => import("react/jsx-runtime").JSX.Element;
|
|
34
40
|
export default PaymentForm;
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
1
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
11
|
import { Button, Form } from '@openedx/paragon';
|
|
3
12
|
import { useIntl } from '@openedx/frontend-base';
|
|
@@ -5,7 +14,8 @@ import PaymentFields from '../PaymentFields/PaymentFields';
|
|
|
5
14
|
import ExpressCheckout from '../ExpressCheckout/ExpressCheckout';
|
|
6
15
|
import { BILLING_COUNTRIES } from '../../constants';
|
|
7
16
|
import messages from './Messages';
|
|
8
|
-
const PaymentForm = ({ billing, setField, numberMountRef, expiryMountRef, cvcMountRef, stripeReady, sessionReady, submitting, onPlaceOrder, alacarteLine = null, }) => {
|
|
17
|
+
const PaymentForm = ({ billing, setField, numberMountRef, expiryMountRef, cvcMountRef, stripeReady, sessionReady, submitting, onPlaceOrder, alacarteLine = null, stripe = null, expressCountry = 'US', onExpressPayment, }) => {
|
|
18
|
+
var _a, _b;
|
|
9
19
|
const { formatMessage } = useIntl();
|
|
10
20
|
// No fallback amount — an unresolved à la carte line means we don't know
|
|
11
21
|
// the price yet, so this is left blank rather than showing a made-up number.
|
|
@@ -28,7 +38,9 @@ const PaymentForm = ({ billing, setField, numberMountRef, expiryMountRef, cvcMou
|
|
|
28
38
|
};
|
|
29
39
|
return (_jsxs("div", { className: "payment-form bg-white", children: [_jsxs("div", { 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-0", children: [_jsx("strong", { className: "d-block text-primary-500", children: formatMessage(messages.needAssistance) }), ' ', _jsx("span", { className: "text-gray-700 gap-y-2", children: formatMessage(messages.needAssistanceBody, {
|
|
30
40
|
link: (_jsx("a", { href: "https://support.edx.org", target: "_blank", rel: "noreferrer", className: "text-gray-700", children: formatMessage(messages.learnerHelpCenter) })),
|
|
31
|
-
}) })] })] }), _jsx(ExpressCheckout, {
|
|
41
|
+
}) })] })] }), _jsx(ExpressCheckout, { stripe: stripe, totalAmountCents: (alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.amountDue)
|
|
42
|
+
? Math.round(alacarteLine.amountDue.amount * 100)
|
|
43
|
+
: null, currency: (_b = (_a = alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.amountDue) === null || _a === void 0 ? void 0 : _a.currency) !== null && _b !== void 0 ? _b : null, country: expressCountry, onPaymentAuthorized: onExpressPayment !== null && onExpressPayment !== void 0 ? onExpressPayment : (() => __awaiter(void 0, void 0, void 0, function* () { })), disabled: isDisabled }), _jsx("div", { className: "payment-form__methods", children: _jsx("div", { className: "payment-form__method payment-form__method--selected", children: _jsxs("div", { className: "payment-form__card-panel", children: [_jsx("h3", { className: "payment-form__card-heading font-weight-bold text-center text-primary-500 mb-3", children: formatMessage(messages.payWithCard) }), _jsx(PaymentFields, { numberMountRef: numberMountRef, expiryMountRef: expiryMountRef, cvcMountRef: cvcMountRef }), _jsxs(Form.Group, { controlId: "country", className: "mt-3 mb-0 payment__field--country", children: [_jsx(Form.Label, { className: "payment--field__label text-gray-900", children: formatMessage(messages.country) }), _jsxs(Form.Control, { as: "select", value: billing.country, onChange: (e) => {
|
|
32
44
|
setField('country', e.target.value);
|
|
33
45
|
}, children: [_jsx("option", { value: "", disabled: true, hidden: true, children: formatMessage(messages.countryPlaceholder) }), BILLING_COUNTRIES.map((country) => {
|
|
34
46
|
return (_jsx("option", { value: country[0], children: country[1] }, country[0]));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PaymentForm.js","sourceRoot":"","sources":["../../../src/components/PaymentForm/PaymentForm.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport type { Ref } from 'react';\nimport { Button, Form } from '@openedx/paragon';\nimport { useIntl } from '@openedx/frontend-base';\nimport PaymentFields from '../PaymentFields/PaymentFields';\nimport ExpressCheckout from '../ExpressCheckout/ExpressCheckout';\nimport type { BillingInfo, CheckoutConsent, SupportedPaymentMethod } from '../../types';\nimport type { AlacarteLine } from '../../types/alacarte';\nimport { 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: SupportedPaymentMethod) => void,\n /** @deprecated Subscription plan price — unused for à la carte (see alacarteLine). */\n priceToShow?: { label?: string, amount?: string } | 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 geolocationUrl?: string,\n /** À la carte checkout line — when present, drives the order total shown. */\n alacarteLine?: AlacarteLine | null,\n}\n\nconst PaymentForm = ({\n billing,\n setField,\n numberMountRef,\n expiryMountRef,\n cvcMountRef,\n stripeReady,\n sessionReady,\n submitting,\n onPlaceOrder,\n alacarteLine = null,\n}: PaymentFormProps) => {\n const { formatMessage } = useIntl();\n\n // No fallback amount — an unresolved à la carte line means we don't know\n // the price yet, so this is left blank rather than showing a made-up number.\n const totalText = alacarteLine?.amountDue\n ? formatMessage(messages.totalAmount, {\n amount: alacarteLine.amountDue.amount.toFixed(2), currency: alacarteLine.amountDue.currency,\n })\n : '';\n\n const isDisabled = submitting || !stripeReady || !sessionReady;\n\n // This design has no terms/consent checkboxes, so there's nothing for the\n // learner to opt into on screen — send a default consent record instead of\n // building it from UI state that doesn't exist here.\n const handlePayNow = () => {\n onPlaceOrder({\n tConsentRecurring: true,\n tConsentUkCa: null,\n tConsentAt: new Date().toISOString(),\n tConsentText: '',\n }, 'card');\n };\n\n return (\n <div className=\"payment-form bg-white\">\n <div>\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-0\">\n <strong className=\"d-block text-primary-500\">\n {formatMessage(messages.needAssistance)}\n </strong>{' '}\n <span className=\"text-gray-700 gap-y-2\">\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 </div>\n\n <ExpressCheckout />\n\n {/* ── Payment method ── */}\n <div className=\"payment-form__methods\">\n <div className=\"payment-form__method payment-form__method--selected\">\n <div className=\"payment-form__card-panel\">\n <h3 className=\"payment-form__card-heading font-weight-bold text-center text-primary-500 mb-3\">\n {formatMessage(messages.payWithCard)}\n </h3>\n <PaymentFields\n numberMountRef={numberMountRef}\n expiryMountRef={expiryMountRef}\n cvcMountRef={cvcMountRef}\n />\n <Form.Group\n controlId=\"country\"\n className=\"mt-3 mb-0 payment__field--country\"\n >\n <Form.Label className=\"payment--field__label text-gray-900\">\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 >\n <option value=\"\" disabled hidden>\n {formatMessage(messages.countryPlaceholder)}\n </option>\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 </div>\n\n {/* ── Order total ── */}\n <div className=\"payment-form__total\">\n <p className=\"payment-form__total-disclaimer text-gray-700 mb-3\">\n {formatMessage(messages.orderDisclaimer)}\n </p>\n <div className=\"payment-form__total-row\">\n <p className=\"mb-0 text-gray-900\">\n <strong className=\"payment-form__total-label\">{formatMessage(messages.total)}</strong>{' '}\n <span className=\"payment-form__total-value\">\n {totalText}\n </span>\n </p>\n <Button\n variant=\"brand\"\n className=\"payment-form__submit\"\n onClick={handlePayNow}\n disabled={isDisabled}\n >\n {formatMessage(submitting ? messages.processing : messages.payNow)}\n </Button>\n </div>\n </div>\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 from 'react';\nimport type { Ref } from 'react';\nimport { Button, Form } from '@openedx/paragon';\nimport { useIntl } from '@openedx/frontend-base';\nimport PaymentFields from '../PaymentFields/PaymentFields';\nimport ExpressCheckout from '../ExpressCheckout/ExpressCheckout';\nimport type { BillingInfo, CheckoutConsent, StripeLike, SupportedPaymentMethod } from '../../types';\nimport type { AlacarteLine } from '../../types/alacarte';\nimport { 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: SupportedPaymentMethod) => void,\n /** @deprecated Subscription plan price — unused for à la carte (see alacarteLine). */\n priceToShow?: { label?: string, amount?: string } | 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 geolocationUrl?: string,\n /** À la carte checkout line — when present, drives the order total shown. */\n alacarteLine?: AlacarteLine | null,\n /** Stripe.js instance for express checkout (Apple Pay / Google Pay). */\n stripe?: StripeLike | null,\n /** Two-letter country code for express checkout initialisation. */\n expressCountry?: string,\n /** Called when the user authorises a wallet payment. */\n onExpressPayment?: (paymentMethodId: string, walletCountry?: string) => Promise<void>,\n}\n\nconst PaymentForm = ({\n billing,\n setField,\n numberMountRef,\n expiryMountRef,\n cvcMountRef,\n stripeReady,\n sessionReady,\n submitting,\n onPlaceOrder,\n alacarteLine = null,\n stripe = null,\n expressCountry = 'US',\n onExpressPayment,\n}: PaymentFormProps) => {\n const { formatMessage } = useIntl();\n\n // No fallback amount — an unresolved à la carte line means we don't know\n // the price yet, so this is left blank rather than showing a made-up number.\n const totalText = alacarteLine?.amountDue\n ? formatMessage(messages.totalAmount, {\n amount: alacarteLine.amountDue.amount.toFixed(2), currency: alacarteLine.amountDue.currency,\n })\n : '';\n\n const isDisabled = submitting || !stripeReady || !sessionReady;\n\n // This design has no terms/consent checkboxes, so there's nothing for the\n // learner to opt into on screen — send a default consent record instead of\n // building it from UI state that doesn't exist here.\n const handlePayNow = () => {\n onPlaceOrder({\n tConsentRecurring: true,\n tConsentUkCa: null,\n tConsentAt: new Date().toISOString(),\n tConsentText: '',\n }, 'card');\n };\n\n return (\n <div className=\"payment-form bg-white\">\n <div>\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-0\">\n <strong className=\"d-block text-primary-500\">\n {formatMessage(messages.needAssistance)}\n </strong>{' '}\n <span className=\"text-gray-700 gap-y-2\">\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 </div>\n\n <ExpressCheckout\n stripe={stripe}\n totalAmountCents={\n alacarteLine?.amountDue\n ? Math.round(alacarteLine.amountDue.amount * 100)\n : null\n }\n currency={alacarteLine?.amountDue?.currency ?? null}\n country={expressCountry}\n onPaymentAuthorized={onExpressPayment ?? (async () => {})}\n disabled={isDisabled}\n />\n\n {/* ── Payment method ── */}\n <div className=\"payment-form__methods\">\n <div className=\"payment-form__method payment-form__method--selected\">\n <div className=\"payment-form__card-panel\">\n <h3 className=\"payment-form__card-heading font-weight-bold text-center text-primary-500 mb-3\">\n {formatMessage(messages.payWithCard)}\n </h3>\n <PaymentFields\n numberMountRef={numberMountRef}\n expiryMountRef={expiryMountRef}\n cvcMountRef={cvcMountRef}\n />\n <Form.Group\n controlId=\"country\"\n className=\"mt-3 mb-0 payment__field--country\"\n >\n <Form.Label className=\"payment--field__label text-gray-900\">\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 >\n <option value=\"\" disabled hidden>\n {formatMessage(messages.countryPlaceholder)}\n </option>\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 </div>\n\n {/* ── Order total ── */}\n <div className=\"payment-form__total\">\n <p className=\"payment-form__total-disclaimer text-gray-700 mb-3\">\n {formatMessage(messages.orderDisclaimer)}\n </p>\n <div className=\"payment-form__total-row\">\n <p className=\"mb-0 text-gray-900\">\n <strong className=\"payment-form__total-label\">{formatMessage(messages.total)}</strong>{' '}\n <span className=\"payment-form__total-value\">\n {totalText}\n </span>\n </p>\n <Button\n variant=\"brand\"\n className=\"payment-form__submit\"\n onClick={handlePayNow}\n disabled={isDisabled}\n >\n {formatMessage(submitting ? messages.processing : messages.payNow)}\n </Button>\n </div>\n </div>\n </div>\n );\n};\n\nexport default PaymentForm;\n"]}
|
|
@@ -25,11 +25,20 @@ export interface UseAlacarteCheckoutResult extends AlacartePhaseState {
|
|
|
25
25
|
/**
|
|
26
26
|
* Accepts price (if needed) + terms, calls tCheckoutPlaceOrder with billing,
|
|
27
27
|
* confirms the card via Stripe.js, then sets ?order_bi_id= to poll tPlacedOrder.
|
|
28
|
+
* Fire-and-forget — errors go to placeOrderError state.
|
|
28
29
|
*/
|
|
29
30
|
placeOrder: (opts: {
|
|
30
31
|
billing: BillingInfo;
|
|
31
32
|
confirmCardPayment: (clientSecret: string) => Promise<void>;
|
|
32
33
|
}) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Same as placeOrder but returns a Promise — used by ExpressCheckout
|
|
36
|
+
* so the wallet sheet can await the result and call ev.complete().
|
|
37
|
+
*/
|
|
38
|
+
placeOrderAsync: (opts: {
|
|
39
|
+
billing: BillingInfo;
|
|
40
|
+
confirmCardPayment: (clientSecret: string) => Promise<void>;
|
|
41
|
+
}) => Promise<void>;
|
|
33
42
|
clearPlaceOrderError: () => void;
|
|
34
43
|
/** When ?order_bi_id= is present, the placed order (or null while polling). */
|
|
35
44
|
placedOrder: PlacedOrder | null;
|
|
@@ -311,34 +311,40 @@ export function useAlacarteCheckout(options) {
|
|
|
311
311
|
},
|
|
312
312
|
});
|
|
313
313
|
}, [(_g = derived.checkout) === null || _g === void 0 ? void 0 : _g.biId, client, manualUpdateMutation]);
|
|
314
|
-
const
|
|
314
|
+
const placeOrderCore = useCallback((opts) => __awaiter(this, void 0, void 0, function* () {
|
|
315
315
|
const checkout = derived.checkout;
|
|
316
|
-
if (!checkout || !client
|
|
317
|
-
|
|
316
|
+
if (!checkout || !client)
|
|
317
|
+
throw new Error('Checkout not ready');
|
|
318
318
|
setPlacingOrder(true);
|
|
319
319
|
setPlaceOrderError(null);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
320
|
+
try {
|
|
321
|
+
const { checkout: updated } = yield placeAlacarteOrder({
|
|
322
|
+
updateCheckout: (args) => client.updateCheckout(args),
|
|
323
|
+
placeCheckoutOrder: (biId, billing) => client.placeCheckoutOrder(biId, billing),
|
|
324
|
+
checkout,
|
|
325
|
+
billing: toAlacarteBillingAddress(opts.billing),
|
|
326
|
+
confirmCardPayment: opts.confirmCardPayment,
|
|
327
|
+
});
|
|
328
328
|
setCheckoutOverride(updated);
|
|
329
|
-
// After Stripe confirms, the webhook places the order — poll tPlacedOrder.
|
|
330
329
|
yield setOrderBiId(updated.biId);
|
|
331
|
-
}
|
|
332
|
-
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
333
332
|
if (err instanceof AlacartePlaceOrderError && err.checkout) {
|
|
334
333
|
setCheckoutOverride(err.checkout);
|
|
335
334
|
}
|
|
336
335
|
setPlaceOrderError(errorMessage(err));
|
|
337
|
-
|
|
338
|
-
|
|
336
|
+
throw err;
|
|
337
|
+
}
|
|
338
|
+
finally {
|
|
339
339
|
setPlacingOrder(false);
|
|
340
|
-
}
|
|
341
|
-
}, [derived.checkout, client,
|
|
340
|
+
}
|
|
341
|
+
}), [derived.checkout, client, setOrderBiId]);
|
|
342
|
+
/** Fire-and-forget wrapper for card payments. */
|
|
343
|
+
const placeOrder = useCallback((opts) => {
|
|
344
|
+
if (placingOrder)
|
|
345
|
+
return;
|
|
346
|
+
placeOrderCore(opts).catch(() => { });
|
|
347
|
+
}, [placeOrderCore, placingOrder]);
|
|
342
348
|
const placedOrder = ((_h = placedOrderQuery.data) === null || _h === void 0 ? void 0 : _h.ok) ? placedOrderQuery.data.order : null;
|
|
343
349
|
const placedOrderError = (() => {
|
|
344
350
|
var _a;
|
|
@@ -355,7 +361,7 @@ export function useAlacarteCheckout(options) {
|
|
|
355
361
|
return Object.assign(Object.assign({}, derived), { couponError, couponApplying: manualUpdateMutation.isPending, applyCoupon,
|
|
356
362
|
removeCoupon, clearCouponError: () => setCouponError(null), placingOrder,
|
|
357
363
|
placeOrderError,
|
|
358
|
-
placeOrder, clearPlaceOrderError: () => setPlaceOrderError(null), placedOrder, awaitingPlacedOrder: !!orderBiId && !placedOrder && !placedOrderError && (placedOrderQuery.isLoading
|
|
364
|
+
placeOrder, placeOrderAsync: placeOrderCore, clearPlaceOrderError: () => setPlaceOrderError(null), placedOrder, awaitingPlacedOrder: !!orderBiId && !placedOrder && !placedOrderError && (placedOrderQuery.isLoading
|
|
359
365
|
|| placedOrderQuery.isFetching
|
|
360
366
|
|| ((_j = placedOrderQuery.data) === null || _j === void 0 ? void 0 : _j.error) === 'NOT_PLACED'
|
|
361
367
|
|| ((_k = placedOrderQuery.data) === null || _k === void 0 ? void 0 : _k.error) === 'ORDER_READ_FAILED'
|