@edx/frontend-app-subscription-checkout 1.22.0 → 1.22.2

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.
@@ -0,0 +1,20 @@
1
+ <svg width="147" height="48" viewBox="0 0 147 48" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_9277_3372)">
3
+ <path d="M39.8927 12.306L41.0971 6.78906H5.79125L0 33.3033H29.5192L27.8949 40.4174H56.1682L62.4071 12.306H39.8927Z" fill="#5A5854"/>
4
+ <path d="M12.0279 29.917C11.2296 29.917 10.4728 29.7931 9.76218 29.541C9.05154 29.2888 8.42858 28.9171 7.90252 28.4305C7.37185 27.9394 6.95654 27.3377 6.64737 26.621C6.33819 25.9043 6.18591 25.0814 6.18591 24.1523C6.18591 22.8825 6.37049 21.7234 6.73966 20.6793C7.10882 19.6352 7.62104 18.7415 8.28092 17.9938C8.9408 17.2461 9.72988 16.6709 10.6482 16.2595C11.5665 15.8525 12.5771 15.6445 13.6707 15.6445C14.409 15.6445 15.1104 15.7684 15.7703 16.0162C16.4348 16.2639 17.0116 16.6223 17.51 17.0957C18.0084 17.5691 18.4052 18.1486 18.6959 18.8432C18.9913 19.5334 19.1343 20.3253 19.1343 21.2235C19.1343 21.3606 19.1297 21.5199 19.1205 21.7013C19.1112 21.8871 19.0974 22.0729 19.0789 22.2631C19.0605 22.4534 19.042 22.6436 19.0282 22.825C19.0097 23.0064 18.9867 23.1657 18.9636 23.2896H8.59932C8.59009 23.409 8.58548 23.524 8.58086 23.6346C8.57625 23.7453 8.57625 23.8603 8.57625 23.9797C8.57625 24.6876 8.68238 25.2893 8.89465 25.7892C9.10692 26.2892 9.38379 26.6962 9.72988 27.0103C10.0714 27.3244 10.459 27.5545 10.8835 27.6961C11.3081 27.8376 11.7418 27.9084 12.1756 27.9084C13.1262 27.9084 13.8922 27.7492 14.469 27.435C15.0458 27.1209 15.4888 26.6918 15.7888 26.1564H18.2299C18.0776 26.6873 17.8284 27.1784 17.4823 27.6386C17.1362 28.0987 16.6978 28.4969 16.1672 28.8331C15.6365 29.1693 15.0228 29.4348 14.326 29.625C13.6338 29.8241 12.8632 29.917 12.0279 29.917ZM16.7117 21.5155C16.7209 21.4712 16.7255 21.396 16.7301 21.2765C16.7348 21.1615 16.7348 21.0465 16.7348 20.9359C16.7348 20.4846 16.6655 20.0555 16.5271 19.6484C16.3887 19.2458 16.181 18.8875 15.9041 18.5822C15.6273 18.2769 15.2858 18.0336 14.8751 17.8522C14.4644 17.6752 13.9891 17.5823 13.4492 17.5823C12.8955 17.5823 12.3832 17.6752 11.9033 17.8655C11.4234 18.0513 10.985 18.3212 10.5974 18.6663C10.2052 19.0114 9.86832 19.4228 9.58222 19.905C9.29612 20.3873 9.07 20.9226 8.90388 21.511L16.7117 21.5155Z" fill="white"/>
5
+ <path d="M25.6883 29.9199C24.9685 29.9199 24.2901 29.7916 23.6579 29.5306C23.0257 29.2696 22.472 28.8979 21.9967 28.4113C21.5214 27.9246 21.143 27.3362 20.8708 26.646C20.5939 25.9558 20.4554 25.186 20.4554 24.3322C20.4554 23.5048 20.5431 22.7129 20.7139 21.9608C20.8892 21.2087 21.1338 20.5052 21.4522 19.8637C21.7706 19.2178 22.1582 18.6382 22.6058 18.1161C23.0534 17.5941 23.5518 17.1517 24.1009 16.7845C24.6501 16.4173 25.2407 16.1341 25.8775 15.9394C26.5143 15.7448 27.1788 15.643 27.871 15.643C28.3878 15.643 28.877 15.7094 29.3431 15.8465C29.8091 15.9837 30.2337 16.1739 30.612 16.4173C30.9904 16.6606 31.3135 16.9526 31.5857 17.2977C31.858 17.6428 32.0564 18.0188 32.181 18.4303H32.3933L34.1699 10.4092H36.5048L32.2364 29.6722H30.0214L30.4367 27.7919H30.2244C29.6984 28.4467 29.0431 28.9643 28.2494 29.3448C27.4557 29.7297 26.602 29.9199 25.6883 29.9199ZM26.422 27.9379C27.1696 27.9379 27.8618 27.7698 28.4986 27.4335C29.1354 27.0973 29.6891 26.6372 30.1598 26.0532C30.6305 25.4692 30.9997 24.7834 31.2673 23.9959C31.535 23.2084 31.6688 22.3545 31.6688 21.4431C31.6688 20.8547 31.5857 20.3282 31.415 19.8549C31.2442 19.3859 31.0043 18.9877 30.6951 18.6603C30.3813 18.3329 30.0075 18.0763 29.5599 17.8994C29.1169 17.718 28.614 17.6295 28.051 17.6295C27.3127 17.6295 26.6297 17.7888 25.9975 18.1029C25.3653 18.417 24.8208 18.855 24.3547 19.4213C23.8933 19.9832 23.5287 20.6556 23.2611 21.4343C22.9934 22.213 22.8596 23.0713 22.8596 24.0003C22.8596 24.5799 22.9473 25.1108 23.1226 25.5975C23.298 26.0841 23.538 26.5 23.8517 26.8451C24.1655 27.1902 24.5347 27.4601 24.9731 27.6503C25.4022 27.8405 25.8868 27.9379 26.422 27.9379Z" fill="white"/>
6
+ <path d="M65.4155 15.8049C65.1617 15.8049 64.9218 15.7607 64.7003 15.6678C64.4788 15.5749 64.2804 15.451 64.1142 15.2873C63.9435 15.1236 63.8143 14.9378 63.7174 14.7254C63.6205 14.5131 63.5743 14.283 63.5743 14.0397C63.5743 13.7963 63.6205 13.5663 63.7174 13.3539C63.8143 13.1416 63.9435 12.9513 64.1142 12.792C64.285 12.6284 64.4788 12.5045 64.7003 12.4116C64.9218 12.3187 65.1617 12.2744 65.4155 12.2744C65.6693 12.2744 65.9093 12.3187 66.1308 12.4116C66.3523 12.5045 66.5507 12.6284 66.7168 12.792C66.8876 12.9557 67.0168 13.1416 67.1137 13.3539C67.2106 13.5663 67.2567 13.7963 67.2567 14.0397C67.2567 14.283 67.2106 14.5131 67.1137 14.7254C67.0168 14.9378 66.8876 15.128 66.7168 15.2873C66.5461 15.4466 66.3523 15.5749 66.1308 15.6678C65.9093 15.7607 65.6693 15.8049 65.4155 15.8049ZM65.4155 15.4775C65.6924 15.4775 65.9462 15.4112 66.1723 15.2829C66.3984 15.1546 66.5784 14.9776 66.7168 14.7608C66.8507 14.544 66.9199 14.3007 66.9199 14.0352C66.9199 13.7698 66.8507 13.5265 66.7168 13.3097C66.583 13.0929 66.3984 12.9203 66.1723 12.7876C65.9462 12.6593 65.6924 12.593 65.4155 12.593C65.1387 12.593 64.8849 12.6593 64.6588 12.7876C64.4326 12.9159 64.2527 13.0929 64.1142 13.3097C63.9804 13.5265 63.9112 13.7698 63.9112 14.0352C63.9112 14.3007 63.9804 14.544 64.1142 14.7608C64.2481 14.9776 64.4326 15.1501 64.6588 15.2829C64.8849 15.4112 65.1387 15.4775 65.4155 15.4775ZM64.788 14.8316V13.1991H65.6047C65.6924 13.1991 65.7755 13.2168 65.8539 13.2566C65.937 13.2964 66.0016 13.3539 66.0523 13.4291C66.1077 13.5043 66.1308 13.5973 66.1308 13.7079C66.1308 13.8185 66.1031 13.9158 66.0477 13.9954C65.9924 14.0751 65.9231 14.137 65.8401 14.1812C65.757 14.2255 65.6693 14.2432 65.5817 14.2432H64.9495V14.022H65.5032C65.5817 14.022 65.6509 13.9954 65.7155 13.9423C65.7755 13.8892 65.8078 13.8096 65.8078 13.7123C65.8078 13.6105 65.7755 13.5353 65.7155 13.4911C65.6555 13.4468 65.5863 13.4247 65.5124 13.4247H65.0833V14.8316H64.788ZM65.7801 14.0751L66.2046 14.8316H65.8724L65.4617 14.0751H65.7801Z" fill="#5A5854"/>
7
+ <path d="M57.4501 15.9307H52.2403L46.5644 22.6732H46.2829L43.2788 15.9307H38.0137L42.5267 25.7259L32.7808 36.7952H37.9167L44.1741 29.6811H44.594L47.9764 36.7952H53.1447L48.1472 26.2125L57.4501 15.9307Z" fill="white"/>
8
+ </g>
9
+ <g opacity="0.7">
10
+ <path d="M136.679 23.9541L131.484 24.0955C131.491 23.7106 131.405 23.3729 131.224 23.0823C131.044 22.7839 130.789 22.5522 130.459 22.3873C130.129 22.2223 129.736 22.1399 129.281 22.1399C128.542 22.1399 127.879 22.3048 127.29 22.6347C126.708 22.9645 126.383 23.3926 126.312 23.9188C126.257 24.2015 126.335 24.4489 126.547 24.6609C126.76 24.8651 127.231 25.0458 127.961 25.2029L131.354 25.8626C133.098 26.2081 134.362 26.8089 135.147 27.665C135.941 28.5132 136.223 29.6363 135.996 31.0343C135.791 32.2281 135.265 33.2608 134.417 34.1326C133.577 35.0044 132.505 35.6798 131.201 36.1589C129.905 36.6301 128.472 36.8657 126.901 36.8657C124.246 36.8657 122.244 36.3316 120.893 35.2635C119.55 34.1876 118.921 32.7621 119.008 30.9872L124.615 30.8458C124.639 31.5134 124.863 32.016 125.287 32.3537C125.719 32.6836 126.304 32.8564 127.042 32.8721C127.851 32.8878 128.55 32.7189 129.139 32.3655C129.728 32.0121 130.058 31.5762 130.129 31.0578C130.176 30.7201 130.062 30.457 129.787 30.2685C129.52 30.08 129.021 29.9112 128.291 29.762L125.228 29.1612C123.469 28.8234 122.196 28.1873 121.411 27.2527C120.634 26.3102 120.367 25.1086 120.61 23.6478C120.814 22.4854 121.293 21.4998 122.047 20.6909C122.809 19.8741 123.803 19.2536 125.028 18.8295C126.261 18.4054 127.667 18.1934 129.245 18.1934C131.774 18.1934 133.683 18.7078 134.971 19.7366C136.267 20.7576 136.836 22.1634 136.679 23.9541Z" fill="#5A5854"/>
11
+ <path d="M112.007 28.7132L113.727 18.4287H119.476L116.46 36.5238H110.97L111.524 33.1545H111.335C110.778 34.2619 109.957 35.1415 108.873 35.7934C107.789 36.4374 106.568 36.7594 105.209 36.7594C103.976 36.7594 102.939 36.4767 102.099 35.9112C101.259 35.3457 100.662 34.5564 100.308 33.5433C99.9629 32.5223 99.9001 31.3285 100.12 29.962L102.052 18.4287H107.813L106.081 28.831C105.932 29.8128 106.061 30.5864 106.47 31.1518C106.886 31.7173 107.538 32 108.425 32C108.999 32 109.537 31.8744 110.039 31.623C110.542 31.3639 110.966 30.9908 111.312 30.5039C111.665 30.0091 111.897 29.4122 112.007 28.7132Z" fill="#5A5854"/>
12
+ <path d="M100.375 12.397L96.3699 36.5238H90.6091L94.6146 12.397H100.375Z" fill="#5A5854"/>
13
+ <path d="M70 36.5238L74.0054 12.397H83.9719C85.7783 12.397 87.2823 12.7465 88.4839 13.4455C89.6855 14.1444 90.5377 15.1222 91.0403 16.3788C91.543 17.6354 91.6568 19.0923 91.382 20.7495C91.1149 22.4223 90.5141 23.8792 89.5795 25.1201C88.6528 26.361 87.459 27.3231 85.9982 28.0063C84.5374 28.6896 82.8763 29.0313 81.015 29.0313H74.8772L75.643 24.4368H80.7087C81.5961 24.4368 82.3619 24.2837 83.0059 23.9774C83.6578 23.6711 84.18 23.243 84.5727 22.6933C84.9654 22.1435 85.2246 21.4956 85.3502 20.7495C85.4681 20.0034 85.4209 19.3593 85.2089 18.8174C84.9968 18.2677 84.6159 17.8436 84.0662 17.5451C83.5242 17.2467 82.8174 17.0975 81.9456 17.0975H79.0711L75.8314 36.5238H70Z" fill="#5A5854"/>
14
+ </g>
15
+ <defs>
16
+ <clipPath id="clip0_9277_3372">
17
+ <rect width="67.2568" height="33.6284" fill="white" transform="translate(0 6.78906)"/>
18
+ </clipPath>
19
+ </defs>
20
+ </svg>
@@ -10,6 +10,8 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import { useEffect } from 'react';
14
+ import { publish } from '@openedx/frontend-base';
13
15
  import { Alert, Col, Row } from '@openedx/paragon';
14
16
  import { Error as ErrorIcon } from '@openedx/paragon/icons';
15
17
  import { useIntl } from '@openedx/frontend-base';
@@ -23,7 +25,11 @@ import { getPlanPrice, getRenewalLabel } from '../../utils/billingDetails';
23
25
  import { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';
24
26
  import { getTrialDateRange } from '../../utils';
25
27
  import { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';
28
+ import { CHECKOUT_PAYMENT_STATE_CHANGED } from '../CheckoutLogos';
26
29
  import messages from './Messages';
30
+ const publishPaymentState = (state) => {
31
+ publish(CHECKOUT_PAYMENT_STATE_CHANGED, state);
32
+ };
27
33
  /** Resolve purchased cycle from subscription details, falling back to checkout selection. */
28
34
  function resolvePurchasedCycle(selectedCycle, planName, renewalType) {
29
35
  const haystack = `${planName !== null && planName !== void 0 ? planName : ''} ${renewalType !== null && renewalType !== void 0 ? renewalType : ''}`.toLowerCase();
@@ -38,6 +44,11 @@ const Checkout = (_a) => {
38
44
  var { homeUrl = '/' } = _a, props = __rest(_a, ["homeUrl"]);
39
45
  const { formatMessage } = useIntl();
40
46
  const c = useCheckout(props);
47
+ const isPaid = c.phase === 'success';
48
+ useEffect(() => {
49
+ publishPaymentState({ isPaid });
50
+ }, [isPaid]);
51
+ useEffect(() => () => publishPaymentState({ isPaid: false }), []);
41
52
  const showProcessing = c.returningFromStripe
42
53
  && c.processingSubscription
43
54
  && c.phase !== 'success'
@@ -1 +1 @@
1
- {"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport { Alert, Col, Row } from '@openedx/paragon';\nimport { Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { 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 { getPlanPrice, getRenewalLabel } from '../../utils/billingDetails';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { getTrialDateRange } from '@src/utils';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle } from '../../types';\nimport messages from './Messages';\n\n/** Resolve purchased cycle from subscription details, falling back to checkout selection. */\nfunction resolvePurchasedCycle(\n selectedCycle: BillingCycle,\n planName?: string | null,\n renewalType?: string | null,\n): BillingCycle {\n const haystack = `${planName ?? ''} ${renewalType ?? ''}`.toLowerCase();\n if (haystack.includes('annual') || haystack.includes('year')) return 'annual';\n if (haystack.includes('month')) return 'monthly';\n return selectedCycle;\n}\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};\n\nconst Checkout = ({\n homeUrl = '/',\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const c = useCheckout(props);\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 monthlyPrice = getPlanPrice(c.billingPlans, 'monthly');\n const annualPrice = getPlanPrice(c.billingPlans, 'annual');\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 const purchasedCycle = resolvePurchasedCycle(\n selectedCycle,\n c.subscriptionDetails?.planName,\n c.subscriptionDetails?.renewalType,\n );\n const purchasedPrice = getPlanPrice(c.billingPlans, purchasedCycle);\n\n const handleStartCourse = () => {\n if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = '/subscription-search';\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 return (\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\">\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 {showForm && (\n <>\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || (c.returningFromStripe\n ? formatMessage(messages.paymentVerificationFailed)\n : formatMessage(messages.paymentFailed))}\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={c.processingSubscription}\n onPlaceOrder={c.placeOrder}\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={\n selectedCycle === 'monthly' ? monthlyPrice : annualPrice\n }\n cycleToShow={\n selectedCycle === 'monthly' ? 'monthly' : 'annual'\n }\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n monthlyRegularAmount={monthlyPrice?.amount}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n trialStart={purchasedCycle === 'monthly' ? displayStart : undefined}\n trialEnd={purchasedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={\n purchasedCycle === 'monthly'\n ? monthlyPrice?.label\n : annualPrice?.label\n }\n cycleToShow={\n purchasedCycle === 'monthly' ? 'monthly' : 'annual'\n }\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 amount={purchasedPrice?.amount}\n monthlyRegularAmount={monthlyPrice?.amount}\n currency={purchasedPrice?.currency}\n cycleToShow={purchasedCycle}\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n monthlyRegularAmount={monthlyPrice?.amount}\n annualAmount={annualPrice?.amount}\n currency={monthlyPrice?.currency ?? annualPrice?.currency}\n monthlyRenewalLabel={getRenewalLabel(c.billingPlans, 'monthly')}\n annualRenewalLabel={getRenewalLabel(c.billingPlans, 'annual')}\n activeSubscriberCount={c.activeSubscriberCount}\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={c.course}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\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, { useEffect } from 'react';\nimport { publish } from '@openedx/frontend-base';\nimport { Alert, Col, Row } from '@openedx/paragon';\nimport { Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { 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 { getPlanPrice, getRenewalLabel } from '../../utils/billingDetails';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { getTrialDateRange } from '@src/utils';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle } from '../../types';\nimport { CHECKOUT_PAYMENT_STATE_CHANGED } from '../CheckoutLogos';\nimport type { CheckoutPaymentState } from '../CheckoutLogos';\nimport messages from './Messages';\n\nconst publishPaymentState = (state: CheckoutPaymentState) => {\n publish(CHECKOUT_PAYMENT_STATE_CHANGED, state);\n};\n\n/** Resolve purchased cycle from subscription details, falling back to checkout selection. */\nfunction resolvePurchasedCycle(\n selectedCycle: BillingCycle,\n planName?: string | null,\n renewalType?: string | null,\n): BillingCycle {\n const haystack = `${planName ?? ''} ${renewalType ?? ''}`.toLowerCase();\n if (haystack.includes('annual') || haystack.includes('year')) return 'annual';\n if (haystack.includes('month')) return 'monthly';\n return selectedCycle;\n}\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};\n\nconst Checkout = ({\n homeUrl = '/',\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const c = useCheckout(props);\n const isPaid = c.phase === 'success';\n\n useEffect(() => {\n publishPaymentState({ isPaid });\n }, [isPaid]);\n\n useEffect(() => () => publishPaymentState({ isPaid: false }), []);\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 monthlyPrice = getPlanPrice(c.billingPlans, 'monthly');\n const annualPrice = getPlanPrice(c.billingPlans, 'annual');\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 const purchasedCycle = resolvePurchasedCycle(\n selectedCycle,\n c.subscriptionDetails?.planName,\n c.subscriptionDetails?.renewalType,\n );\n const purchasedPrice = getPlanPrice(c.billingPlans, purchasedCycle);\n\n const handleStartCourse = () => {\n if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = '/subscription-search';\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 return (\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\">\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 {showForm && (\n <>\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || (c.returningFromStripe\n ? formatMessage(messages.paymentVerificationFailed)\n : formatMessage(messages.paymentFailed))}\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={c.processingSubscription}\n onPlaceOrder={c.placeOrder}\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={\n selectedCycle === 'monthly' ? monthlyPrice : annualPrice\n }\n cycleToShow={\n selectedCycle === 'monthly' ? 'monthly' : 'annual'\n }\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n monthlyRegularAmount={monthlyPrice?.amount}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n trialStart={purchasedCycle === 'monthly' ? displayStart : undefined}\n trialEnd={purchasedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={\n purchasedCycle === 'monthly'\n ? monthlyPrice?.label\n : annualPrice?.label\n }\n cycleToShow={\n purchasedCycle === 'monthly' ? 'monthly' : 'annual'\n }\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 amount={purchasedPrice?.amount}\n monthlyRegularAmount={monthlyPrice?.amount}\n currency={purchasedPrice?.currency}\n cycleToShow={purchasedCycle}\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n monthlyRegularAmount={monthlyPrice?.amount}\n annualAmount={annualPrice?.amount}\n currency={monthlyPrice?.currency ?? annualPrice?.currency}\n monthlyRenewalLabel={getRenewalLabel(c.billingPlans, 'monthly')}\n annualRenewalLabel={getRenewalLabel(c.billingPlans, 'annual')}\n activeSubscriberCount={c.activeSubscriberCount}\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={c.course}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n );\n};\n\nexport default Checkout;\n"]}
@@ -0,0 +1,9 @@
1
+ export declare const CHECKOUT_PAYMENT_STATE_CHANGED = "org.openedx.frontend.event.subscriptionCheckout.paymentStateChanged.v1";
2
+ export interface CheckoutPaymentState {
3
+ isPaid: boolean;
4
+ }
5
+ export declare const SubscriptionCheckoutLogo: () => import("react/jsx-runtime").JSX.Element;
6
+ export declare const MobileCenterCheckoutLogo: () => import("react/jsx-runtime").JSX.Element;
7
+ export declare const MobileLeftPaidLogo: () => import("react/jsx-runtime").JSX.Element | null;
8
+ declare const _default: null;
9
+ export default _default;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useState } from 'react';
3
+ import { getUrlByRouteRole, homeRole, useSiteEvent } from '@openedx/frontend-base';
4
+ import { Link } from 'react-router-dom';
5
+ import { Image } from '@openedx/paragon';
6
+ import logoTrademark from '@openedx/brand-openedx/logo-trademark.svg';
7
+ import EdxDesktop from '../assets/Unlimited-desktop.svg';
8
+ export const CHECKOUT_PAYMENT_STATE_CHANGED = 'org.openedx.frontend.event.subscriptionCheckout.paymentStateChanged.v1';
9
+ export const SubscriptionCheckoutLogo = () => {
10
+ const destinationUrl = getUrlByRouteRole(homeRole) || '/';
11
+ const [isPaid, setIsPaid] = useState(false);
12
+ const handlePaymentStateChanged = useCallback((_topic, data) => setIsPaid(!!(data === null || data === void 0 ? void 0 : data.isPaid)), []);
13
+ useSiteEvent(CHECKOUT_PAYMENT_STATE_CHANGED, handlePaymentStateChanged);
14
+ const paidLogoUrl = EdxDesktop;
15
+ const showPaidLogo = isPaid;
16
+ return (_jsx(Link, { to: destinationUrl, className: "p-0", children: showPaidLogo ? (_jsx(Image, { src: paidLogoUrl, alt: "edX Plus", style: { width: '147px', height: '48px' } })) : (_jsx(Image, { src: logoTrademark, alt: "edX", style: { width: '87.37px', height: '48px' } })) }));
17
+ };
18
+ export const MobileCenterCheckoutLogo = () => {
19
+ const destinationUrl = getUrlByRouteRole(homeRole) || '/';
20
+ const [isPaid, setIsPaid] = useState(false);
21
+ const handlePaymentStateChanged = useCallback((_topic, data) => setIsPaid(!!(data === null || data === void 0 ? void 0 : data.isPaid)), []);
22
+ useSiteEvent(CHECKOUT_PAYMENT_STATE_CHANGED, handlePaymentStateChanged);
23
+ if (isPaid)
24
+ return _jsx(_Fragment, {});
25
+ return (_jsx(Link, { to: destinationUrl, className: "p-0", children: _jsx(Image, { src: logoTrademark, alt: "edX", style: { width: '87.37px', height: '48px' } }) }));
26
+ };
27
+ export const MobileLeftPaidLogo = () => {
28
+ const destinationUrl = getUrlByRouteRole(homeRole) || '/';
29
+ const [isPaid, setIsPaid] = useState(false);
30
+ const handlePaymentStateChanged = useCallback((_topic, data) => setIsPaid(!!(data === null || data === void 0 ? void 0 : data.isPaid)), []);
31
+ useSiteEvent(CHECKOUT_PAYMENT_STATE_CHANGED, handlePaymentStateChanged);
32
+ if (!isPaid)
33
+ return null;
34
+ return (_jsx(Link, { to: destinationUrl, className: "p-0", children: _jsx(Image, { src: EdxDesktop, alt: "edX Plus", style: { width: '147px', height: 'auto' } }) }));
35
+ };
36
+ export default null;
37
+ //# sourceMappingURL=CheckoutLogos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CheckoutLogos.js","sourceRoot":"","sources":["../../src/components/CheckoutLogos.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useState } from 'react';\nimport { getUrlByRouteRole, homeRole, useSiteEvent } from '@openedx/frontend-base';\nimport { Link } from 'react-router-dom';\nimport { Image } from '@openedx/paragon';\nimport logoTrademark from '@openedx/brand-openedx/logo-trademark.svg';\nimport EdxDesktop from '../assets/Unlimited-desktop.svg';\n\nexport const CHECKOUT_PAYMENT_STATE_CHANGED = 'org.openedx.frontend.event.subscriptionCheckout.paymentStateChanged.v1';\n\nexport interface CheckoutPaymentState {\n isPaid: boolean,\n}\n\nexport const SubscriptionCheckoutLogo = () => {\n const destinationUrl = getUrlByRouteRole(homeRole) || '/';\n const [isPaid, setIsPaid] = useState(false);\n\n const handlePaymentStateChanged = useCallback(\n (_topic: string, data?: CheckoutPaymentState) => setIsPaid(!!data?.isPaid),\n [],\n );\n useSiteEvent(CHECKOUT_PAYMENT_STATE_CHANGED, handlePaymentStateChanged);\n\n const paidLogoUrl = EdxDesktop;\n const showPaidLogo = isPaid;\n\n return (\n <Link to={destinationUrl} className=\"p-0\">\n {showPaidLogo ? (\n <Image src={paidLogoUrl} alt=\"edX Plus\" style={{ width: '147px', height: '48px' }} />\n ) : (\n <Image\n src={logoTrademark}\n alt=\"edX\"\n style={{ width: '87.37px', height: '48px' }}\n />\n )}\n </Link>\n );\n};\n\nexport const MobileCenterCheckoutLogo = () => {\n const destinationUrl = getUrlByRouteRole(homeRole) || '/';\n const [isPaid, setIsPaid] = useState(false);\n\n const handlePaymentStateChanged = useCallback(\n (_topic: string, data?: CheckoutPaymentState) => setIsPaid(!!data?.isPaid),\n [],\n );\n useSiteEvent(CHECKOUT_PAYMENT_STATE_CHANGED, handlePaymentStateChanged);\n\n if (isPaid) return <></>;\n\n return (\n <Link to={destinationUrl} className=\"p-0\">\n <Image\n src={logoTrademark}\n alt=\"edX\"\n style={{ width: '87.37px', height: '48px' }}\n />\n </Link>\n );\n};\n\nexport const MobileLeftPaidLogo = () => {\n const destinationUrl = getUrlByRouteRole(homeRole) || '/';\n const [isPaid, setIsPaid] = useState(false);\n\n const handlePaymentStateChanged = useCallback(\n (_topic: string, data?: CheckoutPaymentState) => setIsPaid(!!data?.isPaid),\n [],\n );\n useSiteEvent(CHECKOUT_PAYMENT_STATE_CHANGED, handlePaymentStateChanged);\n\n if (!isPaid) return null;\n\n return (\n <Link to={destinationUrl} className=\"p-0\">\n <Image src={EdxDesktop} alt=\"edX Plus\" style={{ width: '147px', height: 'auto' }} />\n </Link>\n );\n};\n\nexport default null;\n"]}
@@ -31,14 +31,63 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
31
31
  * The backend then drives bpStatus to STRIPE_CONFIRMATION_REQUIRED and
32
32
  * populates tPaypalApprovalUrl, which useCheckout redirects to as the
33
33
  * hosted confirmation step for PayPal.
34
+ *
35
+ * One open cart per learner (SUBS-1127): step 2 is refused while an earlier
36
+ * cart is still open, e.g. one left behind when the learner came back from
37
+ * PayPal without approving. See createCartReplacingOpen.
34
38
  */
35
39
  import { useMutation, useQueryClient } from '@tanstack/react-query';
36
40
  import { STATUS } from '../../constants';
37
41
  import { checkoutKeys } from './keys';
38
42
  import { getPlanPrice } from '../../utils/billingDetails';
39
43
  import { buildStripeReturnUrl } from '../../utils/stripe';
44
+ import { parseCartRefusal, CART_IN_PROGRESS_STATES, ALREADY_SUBSCRIBED_MESSAGE, PREVIOUS_ORDER_PROCESSING_MESSAGE, PREVIOUS_ORDER_STUCK_MESSAGE, } from '../../utils/cartRefusal';
40
45
  class CardValidationError extends Error {
41
46
  }
47
+ /** A second refusal after clearing the open cart: say why, never the generic validation text. */
48
+ function refusalMessage(err) {
49
+ const refusal = parseCartRefusal(err);
50
+ if ((refusal === null || refusal === void 0 ? void 0 : refusal.code) === 'ACTIVE_SUBSCRIPTION')
51
+ return new Error(ALREADY_SUBSCRIBED_MESSAGE);
52
+ if ((refusal === null || refusal === void 0 ? void 0 : refusal.code) === 'OPEN_CART_EXISTS')
53
+ return new Error(PREVIOUS_ORDER_STUCK_MESSAGE);
54
+ return err instanceof Error ? err : new Error(String(err));
55
+ }
56
+ /**
57
+ * Create the cart. When the learner still has an earlier open cart (SUBS-1127 —
58
+ * e.g. they left PayPal without approving), cancel it and create the cart once
59
+ * more, so the new attempt starts a fresh approval flow. A cart whose payment
60
+ * is still under way is left alone: cancelling it could orphan a subscription.
61
+ */
62
+ function createCartReplacingOpen(client, ids) {
63
+ return __awaiter(this, void 0, void 0, function* () {
64
+ try {
65
+ return yield client.createCart(ids);
66
+ }
67
+ catch (err) {
68
+ const refusal = parseCartRefusal(err);
69
+ if (!refusal)
70
+ throw err;
71
+ if (refusal.code === 'ACTIVE_SUBSCRIPTION')
72
+ throw new Error(ALREADY_SUBSCRIBED_MESSAGE);
73
+ const openCart = yield client.getCartStatus(refusal.biId).catch(() => null);
74
+ if ((openCart === null || openCart === void 0 ? void 0 : openCart.status) && CART_IN_PROGRESS_STATES.includes(openCart.status)) {
75
+ throw new Error(PREVIOUS_ORDER_PROCESSING_MESSAGE);
76
+ }
77
+ const cancelled = yield client.cancelCart(refusal.biId).catch(() => null);
78
+ // CART_NOT_OPEN: the cart finished on its own meanwhile, so nothing blocks the retry.
79
+ if (!(cancelled === null || cancelled === void 0 ? void 0 : cancelled.ok) && (cancelled === null || cancelled === void 0 ? void 0 : cancelled.error) !== 'CART_NOT_OPEN') {
80
+ throw new Error(PREVIOUS_ORDER_STUCK_MESSAGE);
81
+ }
82
+ }
83
+ try {
84
+ return yield client.createCart(ids);
85
+ }
86
+ catch (err) {
87
+ throw refusalMessage(err);
88
+ }
89
+ });
90
+ }
42
91
  function toCartAddress(b) {
43
92
  return {
44
93
  tFirstName: b.firstName,
@@ -106,7 +155,7 @@ export function useCheckoutMutations(ctx) {
106
155
  }
107
156
  const billingPlanUid = selectedPlan.uid;
108
157
  // ── Step 2: Create the cart ───────────────────────────────────────────
109
- const createdCart = yield client.createCart({ customerUid, salableUid, billingPlanUid });
158
+ const createdCart = yield createCartReplacingOpen(client, { customerUid, salableUid, billingPlanUid });
110
159
  ctx.onCartCreated(createdCart);
111
160
  // ── Step 3: Attach billing address + payment method to cart item ──────
112
161
  const { cartItemUid } = yield client.getCartItem(createdCart.biId);
@@ -1 +1 @@
1
- {"version":3,"file":"useCheckoutMutations.js","sourceRoot":"","sources":["../../../src/data/hooks/useCheckoutMutations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * query/useCheckoutMutations.ts — write-side TanStack mutations.\n *\n * The placeOrder mutation drives the checkout sequence on button click.\n * The viax token and customerUid are established on page load via\n * GET /edx-session and passed in through sessionRef.\n *\n * Card:\n * 1. getSalableUid + getBillingPlans (parallel)\n * 2. createTwouCart\n * 3. stripe.createPaymentMethod → pm_xxx\n * 4. updateTwouCartItem → billing address + pm_xxx\n * 5. updateTwouCart → consent fields (SUBS-228)\n * 6. startBusinessProcessableTransitionV2('Sanction_Check')\n *\n * PayPal (mirrors the card sequence, skipping card tokenization):\n * 1. getSalableUid + getBillingPlans (parallel)\n * 2. createTwouCart\n * 3. updateTwouCartItem → billing address + TwouPaypalPayment\n * 4. updateTwouCart → consent fields + return URL (SUBS-228)\n * 5. startBusinessProcessableTransitionV2('Sanction_Check')\n * The backend then drives bpStatus to STRIPE_CONFIRMATION_REQUIRED and\n * populates tPaypalApprovalUrl, which useCheckout redirects to as the\n * hosted confirmation step for PayPal.\n */\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport type { RefObject } from 'react';\nimport { STATUS } from '../../constants';\nimport { checkoutKeys } from './keys';\nimport type { EdxSessionResult } from '../../utils/provision';\nimport { getPlanPrice } from '../../utils/billingDetails';\nimport { buildStripeReturnUrl } from '../../utils/stripe';\nimport type {\n CheckoutClient, StripeLike, StripeElementLike, Cart, BillingInfo,\n CartBillingAddress, CartStatusResult, CheckoutConsent, BillingCycle, SupportedPaymentMethod,\n} from '../../types';\n\nclass CardValidationError extends Error {}\n\nfunction toCartAddress(b: BillingInfo): CartBillingAddress {\n return {\n tFirstName: b.firstName,\n tLastName: b.lastName,\n upaLine1: b.line1,\n upaCity: b.city,\n upaPostcode: b.zip,\n gadStateOrProvince: b.state,\n gadCountry: { iso2Code: b.country },\n };\n}\n\nfunction toStripeAddress(b: BillingInfo): Record<string, string | undefined> {\n return {\n line1: b.line1 || undefined,\n city: b.city || undefined,\n state: b.state || undefined,\n postal_code: b.zip || undefined,\n country: b.country || undefined,\n };\n}\n\nexport interface CheckoutMutationsCtx {\n client: CheckoutClient | null,\n sessionRef: RefObject<EdxSessionResult | null>,\n stripeRef: RefObject<StripeLike | null>,\n cardNumberElRef: RefObject<StripeElementLike | null>,\n billingRef: RefObject<BillingInfo | null>,\n /** Selected monthly/annual cycle at Place order time. */\n selectedCycleRef: RefObject<BillingCycle>,\n setError: (msg: string) => void,\n setCardError: (msg: string) => void,\n onCartCreated: (cart: Cart) => void,\n}\n\nexport interface PlaceOrderArgs {\n consent: CheckoutConsent,\n paymentMethod: SupportedPaymentMethod,\n}\n\nexport function useCheckoutMutations(ctx: CheckoutMutationsCtx) {\n const queryClient = useQueryClient();\n\n const optimisticConfirm = (cartBiId: string) => {\n queryClient.setQueryData<CartStatusResult>(checkoutKeys.status(cartBiId), (old) => {\n console.log('old optimisticConfirm', old);\n return {\n status: STATUS.CONFIRM,\n stripeSubscriptionId: old?.stripeSubscriptionId ?? null,\n stripeConfirmationRedirectUrl: old?.stripeConfirmationRedirectUrl ?? null,\n paypalApprovalUrl: old?.paypalApprovalUrl ?? null,\n lastComment: old?.lastComment ?? '',\n };\n });\n queryClient.invalidateQueries({ queryKey: checkoutKeys.status(cartBiId) });\n };\n\n const placeOrder = useMutation<Cart, Error, PlaceOrderArgs>({\n mutationFn: async ({ consent, paymentMethod }) => {\n const { client, billingRef } = ctx;\n if (!client) throw new Error('Checkout is not ready yet.');\n\n // Google Pay is not currently supported.\n // if (paymentMethod === 'googlepay') {\n // throw new Error('Google Pay is not supported yet. Please choose Card or PayPal.');\n // }\n\n const billing = billingRef.current;\n if (!billing) throw new Error('Billing information is not available. Please refresh and try again.');\n\n const session = ctx.sessionRef.current;\n if (!session) {\n throw new Error('Checkout session is not ready yet. Please wait a moment and try again.');\n }\n const { customerUid } = session;\n\n // ── Step 1: Fetch salable(product) uid + billing plans in parallel ─────────────\n const [productInfo, plansResult] = await Promise.all([\n client.getSalableUid('TWOU-SUB-PLAN-001'),\n client.getBillingPlans(),\n ]);\n const salableUid = productInfo.uid;\n const selectedCycle = ctx.selectedCycleRef.current ?? 'monthly';\n const selectedPlan = getPlanPrice(plansResult.plans, selectedCycle);\n if (!selectedPlan) {\n throw new Error(`No billing plan found for ${selectedCycle} billing.`);\n }\n const billingPlanUid = selectedPlan.uid;\n\n // ── Step 2: Create the cart ───────────────────────────────────────────\n const createdCart = await client.createCart({ customerUid, salableUid, billingPlanUid });\n ctx.onCartCreated(createdCart);\n\n // ── Step 3: Attach billing address + payment method to cart item ──────\n const { cartItemUid } = await client.getCartItem(createdCart.biId);\n\n if (paymentMethod === 'paypal') {\n // No card tokenization — PayPal issues a hosted approval redirect instead.\n await client.updateCartItem({\n cartItemUid,\n billingAddress: toCartAddress(billing),\n paypalEmail: session.email ?? null,\n });\n } else {\n const stripe = ctx.stripeRef.current;\n const card = ctx.cardNumberElRef.current;\n if (!stripe || !card) throw new Error('Checkout is not ready yet.');\n\n // Tokenize the card (browser → Stripe).\n const { paymentMethod: stripePaymentMethod, error } = await stripe.createPaymentMethod({\n type: 'card',\n card,\n billing_details: {\n name: `${billing.firstName} ${billing.lastName}`.trim(),\n address: toStripeAddress(billing),\n },\n });\n if (error || !stripePaymentMethod) {\n throw new CardValidationError(error?.message || 'There was a problem with your card.');\n }\n\n await client.updateCartItem({\n cartItemUid,\n billingAddress: toCartAddress(billing),\n paymentMethodId: stripePaymentMethod.id,\n });\n }\n\n // ── Step 4: Persist consent + hosted-payment return URL on the TwouCart BI ────\n await client.updateCartConsent({\n cartUid: createdCart.uid,\n consent,\n returnUrl: buildStripeReturnUrl(createdCart.biId),\n });\n\n // ── Step 5: Wait for the asynchronously-created workflow ──────────────\n await client.waitForCartBusinessProcess(createdCart.biId);\n\n // ── Step 6: Fire the backend workflow ─────────────────────────────────\n await client.placeOrder(createdCart.uid);\n\n return createdCart;\n },\n onSuccess: (cart) => {\n optimisticConfirm(cart.biId);\n },\n onError: (err) => {\n if (err instanceof CardValidationError) ctx.setCardError(err.message);\n else ctx.setError(err.message);\n },\n });\n\n return { placeOrder };\n}\n"]}
1
+ {"version":3,"file":"useCheckoutMutations.js","sourceRoot":"","sources":["../../../src/data/hooks/useCheckoutMutations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * query/useCheckoutMutations.ts — write-side TanStack mutations.\n *\n * The placeOrder mutation drives the checkout sequence on button click.\n * The viax token and customerUid are established on page load via\n * GET /edx-session and passed in through sessionRef.\n *\n * Card:\n * 1. getSalableUid + getBillingPlans (parallel)\n * 2. createTwouCart\n * 3. stripe.createPaymentMethod → pm_xxx\n * 4. updateTwouCartItem → billing address + pm_xxx\n * 5. updateTwouCart → consent fields (SUBS-228)\n * 6. startBusinessProcessableTransitionV2('Sanction_Check')\n *\n * PayPal (mirrors the card sequence, skipping card tokenization):\n * 1. getSalableUid + getBillingPlans (parallel)\n * 2. createTwouCart\n * 3. updateTwouCartItem → billing address + TwouPaypalPayment\n * 4. updateTwouCart → consent fields + return URL (SUBS-228)\n * 5. startBusinessProcessableTransitionV2('Sanction_Check')\n * The backend then drives bpStatus to STRIPE_CONFIRMATION_REQUIRED and\n * populates tPaypalApprovalUrl, which useCheckout redirects to as the\n * hosted confirmation step for PayPal.\n *\n * One open cart per learner (SUBS-1127): step 2 is refused while an earlier\n * cart is still open, e.g. one left behind when the learner came back from\n * PayPal without approving. See createCartReplacingOpen.\n */\nimport { useMutation, useQueryClient } from '@tanstack/react-query';\nimport type { RefObject } from 'react';\nimport { STATUS } from '../../constants';\nimport { checkoutKeys } from './keys';\nimport type { EdxSessionResult } from '../../utils/provision';\nimport { getPlanPrice } from '../../utils/billingDetails';\nimport { buildStripeReturnUrl } from '../../utils/stripe';\nimport {\n parseCartRefusal, CART_IN_PROGRESS_STATES, ALREADY_SUBSCRIBED_MESSAGE,\n PREVIOUS_ORDER_PROCESSING_MESSAGE, PREVIOUS_ORDER_STUCK_MESSAGE,\n} from '../../utils/cartRefusal';\nimport type {\n CheckoutClient, StripeLike, StripeElementLike, Cart, CartIds, BillingInfo,\n CartBillingAddress, CartStatusResult, CheckoutConsent, BillingCycle, SupportedPaymentMethod,\n} from '../../types';\n\nclass CardValidationError extends Error {}\n\n/** A second refusal after clearing the open cart: say why, never the generic validation text. */\nfunction refusalMessage(err: unknown): Error {\n const refusal = parseCartRefusal(err);\n if (refusal?.code === 'ACTIVE_SUBSCRIPTION') return new Error(ALREADY_SUBSCRIBED_MESSAGE);\n if (refusal?.code === 'OPEN_CART_EXISTS') return new Error(PREVIOUS_ORDER_STUCK_MESSAGE);\n return err instanceof Error ? err : new Error(String(err));\n}\n\n/**\n * Create the cart. When the learner still has an earlier open cart (SUBS-1127 —\n * e.g. they left PayPal without approving), cancel it and create the cart once\n * more, so the new attempt starts a fresh approval flow. A cart whose payment\n * is still under way is left alone: cancelling it could orphan a subscription.\n */\nasync function createCartReplacingOpen(client: CheckoutClient, ids: CartIds): Promise<Cart> {\n try {\n return await client.createCart(ids);\n } catch (err) {\n const refusal = parseCartRefusal(err);\n if (!refusal) throw err;\n if (refusal.code === 'ACTIVE_SUBSCRIPTION') throw new Error(ALREADY_SUBSCRIBED_MESSAGE);\n\n const openCart = await client.getCartStatus(refusal.biId).catch(() => null);\n if (openCart?.status && CART_IN_PROGRESS_STATES.includes(openCart.status)) {\n throw new Error(PREVIOUS_ORDER_PROCESSING_MESSAGE);\n }\n\n const cancelled = await client.cancelCart(refusal.biId).catch(() => null);\n // CART_NOT_OPEN: the cart finished on its own meanwhile, so nothing blocks the retry.\n if (!cancelled?.ok && cancelled?.error !== 'CART_NOT_OPEN') {\n throw new Error(PREVIOUS_ORDER_STUCK_MESSAGE);\n }\n }\n\n try {\n return await client.createCart(ids);\n } catch (err) {\n throw refusalMessage(err);\n }\n}\n\nfunction toCartAddress(b: BillingInfo): CartBillingAddress {\n return {\n tFirstName: b.firstName,\n tLastName: b.lastName,\n upaLine1: b.line1,\n upaCity: b.city,\n upaPostcode: b.zip,\n gadStateOrProvince: b.state,\n gadCountry: { iso2Code: b.country },\n };\n}\n\nfunction toStripeAddress(b: BillingInfo): Record<string, string | undefined> {\n return {\n line1: b.line1 || undefined,\n city: b.city || undefined,\n state: b.state || undefined,\n postal_code: b.zip || undefined,\n country: b.country || undefined,\n };\n}\n\nexport interface CheckoutMutationsCtx {\n client: CheckoutClient | null,\n sessionRef: RefObject<EdxSessionResult | null>,\n stripeRef: RefObject<StripeLike | null>,\n cardNumberElRef: RefObject<StripeElementLike | null>,\n billingRef: RefObject<BillingInfo | null>,\n /** Selected monthly/annual cycle at Place order time. */\n selectedCycleRef: RefObject<BillingCycle>,\n setError: (msg: string) => void,\n setCardError: (msg: string) => void,\n onCartCreated: (cart: Cart) => void,\n}\n\nexport interface PlaceOrderArgs {\n consent: CheckoutConsent,\n paymentMethod: SupportedPaymentMethod,\n}\n\nexport function useCheckoutMutations(ctx: CheckoutMutationsCtx) {\n const queryClient = useQueryClient();\n\n const optimisticConfirm = (cartBiId: string) => {\n queryClient.setQueryData<CartStatusResult>(checkoutKeys.status(cartBiId), (old) => {\n console.log('old optimisticConfirm', old);\n return {\n status: STATUS.CONFIRM,\n stripeSubscriptionId: old?.stripeSubscriptionId ?? null,\n stripeConfirmationRedirectUrl: old?.stripeConfirmationRedirectUrl ?? null,\n paypalApprovalUrl: old?.paypalApprovalUrl ?? null,\n lastComment: old?.lastComment ?? '',\n };\n });\n queryClient.invalidateQueries({ queryKey: checkoutKeys.status(cartBiId) });\n };\n\n const placeOrder = useMutation<Cart, Error, PlaceOrderArgs>({\n mutationFn: async ({ consent, paymentMethod }) => {\n const { client, billingRef } = ctx;\n if (!client) throw new Error('Checkout is not ready yet.');\n\n // Google Pay is not currently supported.\n // if (paymentMethod === 'googlepay') {\n // throw new Error('Google Pay is not supported yet. Please choose Card or PayPal.');\n // }\n\n const billing = billingRef.current;\n if (!billing) throw new Error('Billing information is not available. Please refresh and try again.');\n\n const session = ctx.sessionRef.current;\n if (!session) {\n throw new Error('Checkout session is not ready yet. Please wait a moment and try again.');\n }\n const { customerUid } = session;\n\n // ── Step 1: Fetch salable(product) uid + billing plans in parallel ─────────────\n const [productInfo, plansResult] = await Promise.all([\n client.getSalableUid('TWOU-SUB-PLAN-001'),\n client.getBillingPlans(),\n ]);\n const salableUid = productInfo.uid;\n const selectedCycle = ctx.selectedCycleRef.current ?? 'monthly';\n const selectedPlan = getPlanPrice(plansResult.plans, selectedCycle);\n if (!selectedPlan) {\n throw new Error(`No billing plan found for ${selectedCycle} billing.`);\n }\n const billingPlanUid = selectedPlan.uid;\n\n // ── Step 2: Create the cart ───────────────────────────────────────────\n const createdCart = await createCartReplacingOpen(client, { customerUid, salableUid, billingPlanUid });\n ctx.onCartCreated(createdCart);\n\n // ── Step 3: Attach billing address + payment method to cart item ──────\n const { cartItemUid } = await client.getCartItem(createdCart.biId);\n\n if (paymentMethod === 'paypal') {\n // No card tokenization — PayPal issues a hosted approval redirect instead.\n await client.updateCartItem({\n cartItemUid,\n billingAddress: toCartAddress(billing),\n paypalEmail: session.email ?? null,\n });\n } else {\n const stripe = ctx.stripeRef.current;\n const card = ctx.cardNumberElRef.current;\n if (!stripe || !card) throw new Error('Checkout is not ready yet.');\n\n // Tokenize the card (browser → Stripe).\n const { paymentMethod: stripePaymentMethod, error } = await stripe.createPaymentMethod({\n type: 'card',\n card,\n billing_details: {\n name: `${billing.firstName} ${billing.lastName}`.trim(),\n address: toStripeAddress(billing),\n },\n });\n if (error || !stripePaymentMethod) {\n throw new CardValidationError(error?.message || 'There was a problem with your card.');\n }\n\n await client.updateCartItem({\n cartItemUid,\n billingAddress: toCartAddress(billing),\n paymentMethodId: stripePaymentMethod.id,\n });\n }\n\n // ── Step 4: Persist consent + hosted-payment return URL on the TwouCart BI ────\n await client.updateCartConsent({\n cartUid: createdCart.uid,\n consent,\n returnUrl: buildStripeReturnUrl(createdCart.biId),\n });\n\n // ── Step 5: Wait for the asynchronously-created workflow ──────────────\n await client.waitForCartBusinessProcess(createdCart.biId);\n\n // ── Step 6: Fire the backend workflow ─────────────────────────────────\n await client.placeOrder(createdCart.uid);\n\n return createdCart;\n },\n onSuccess: (cart) => {\n optimisticConfirm(cart.biId);\n },\n onError: (err) => {\n if (err instanceof CardValidationError) ctx.setCardError(err.message);\n else ctx.setError(err.message);\n },\n });\n\n return { placeOrder };\n}\n"]}
package/dist/slots.js CHANGED
@@ -1,3 +1,38 @@
1
- const slots = [];
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { WidgetOperationTypes } from '@openedx/frontend-base';
3
+ import { SubscriptionCheckoutLogo, MobileCenterCheckoutLogo, MobileLeftPaidLogo } from './components/CheckoutLogos';
4
+ import { subscriptionCheckoutRole } from './constants';
5
+ const slots = [
6
+ {
7
+ slotId: 'org.openedx.frontend.slot.header.desktopLeft.v1',
8
+ id: 'org.openedx.frontend.widget.subscriptionCheckout.header.desktopLogo.v1',
9
+ op: WidgetOperationTypes.REPLACE,
10
+ relatedId: 'org.openedx.frontend.widget.header.desktopLogo.v1',
11
+ element: _jsx(SubscriptionCheckoutLogo, {}),
12
+ condition: {
13
+ active: [subscriptionCheckoutRole],
14
+ },
15
+ },
16
+ {
17
+ slotId: 'org.openedx.frontend.slot.header.mobileCenter.v1',
18
+ id: 'org.openedx.frontend.widget.subscriptionCheckout.header.mobileLogo.v1',
19
+ op: WidgetOperationTypes.REPLACE,
20
+ relatedId: 'org.openedx.frontend.widget.header.mobileLogo.v1',
21
+ element: _jsx(MobileCenterCheckoutLogo, {}),
22
+ condition: {
23
+ active: [subscriptionCheckoutRole],
24
+ },
25
+ },
26
+ {
27
+ slotId: 'org.openedx.frontend.slot.header.mobileLeft.v1',
28
+ id: 'org.openedx.frontend.widget.subscriptionCheckout.header.mobileLogoLeft.v1',
29
+ op: WidgetOperationTypes.APPEND,
30
+ // checkout-specific mobile-left widget: shows paid logo after payment
31
+ element: _jsx(MobileLeftPaidLogo, {}),
32
+ condition: {
33
+ active: [subscriptionCheckoutRole],
34
+ },
35
+ },
36
+ ];
2
37
  export default slots;
3
38
  //# sourceMappingURL=slots.js.map
package/dist/slots.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"slots.js","sourceRoot":"","sources":["../src/slots.tsx"],"names":[],"mappings":"AAEA,MAAM,KAAK,GAAoB,EAAE,CAAC;AAElC,eAAe,KAAK,CAAC","sourcesContent":["import { SlotOperation } from '@openedx/frontend-base';\n\nconst slots: SlotOperation[] = [];\n\nexport default slots;\n"]}
1
+ {"version":3,"file":"slots.js","sourceRoot":"","sources":["../src/slots.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAiB,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACpH,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAEvD,MAAM,KAAK,GAAoB;IAC7B;QACE,MAAM,EAAE,iDAAiD;QACzD,EAAE,EAAE,wEAAwE;QAC5E,EAAE,EAAE,oBAAoB,CAAC,OAAO;QAChC,SAAS,EAAE,mDAAmD;QAC9D,OAAO,EAAE,KAAC,wBAAwB,KAAG;QACrC,SAAS,EAAE;YACT,MAAM,EAAE,CAAC,wBAAwB,CAAC;SACnC;KACF;IACD;QACE,MAAM,EAAE,kDAAkD;QAC1D,EAAE,EAAE,uEAAuE;QAC3E,EAAE,EAAE,oBAAoB,CAAC,OAAO;QAChC,SAAS,EAAE,kDAAkD;QAC7D,OAAO,EAAE,KAAC,wBAAwB,KAAG;QACrC,SAAS,EAAE;YACT,MAAM,EAAE,CAAC,wBAAwB,CAAC;SACnC;KACF;IACD;QACE,MAAM,EAAE,gDAAgD;QACxD,EAAE,EAAE,2EAA2E;QAC/E,EAAE,EAAE,oBAAoB,CAAC,MAAM;QAC/B,sEAAsE;QACtE,OAAO,EAAE,KAAC,kBAAkB,KAAG;QAC/B,SAAS,EAAE;YACT,MAAM,EAAE,CAAC,wBAAwB,CAAC;SACnC;KACF;CACF,CAAC;AAEF,eAAe,KAAK,CAAC","sourcesContent":["import { SlotOperation, WidgetOperationTypes } from '@openedx/frontend-base';\nimport { SubscriptionCheckoutLogo, MobileCenterCheckoutLogo, MobileLeftPaidLogo } from './components/CheckoutLogos';\nimport { subscriptionCheckoutRole } from './constants';\n\nconst slots: SlotOperation[] = [\n {\n slotId: 'org.openedx.frontend.slot.header.desktopLeft.v1',\n id: 'org.openedx.frontend.widget.subscriptionCheckout.header.desktopLogo.v1',\n op: WidgetOperationTypes.REPLACE,\n relatedId: 'org.openedx.frontend.widget.header.desktopLogo.v1',\n element: <SubscriptionCheckoutLogo />,\n condition: {\n active: [subscriptionCheckoutRole],\n },\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileCenter.v1',\n id: 'org.openedx.frontend.widget.subscriptionCheckout.header.mobileLogo.v1',\n op: WidgetOperationTypes.REPLACE,\n relatedId: 'org.openedx.frontend.widget.header.mobileLogo.v1',\n element: <MobileCenterCheckoutLogo />,\n condition: {\n active: [subscriptionCheckoutRole],\n },\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileLeft.v1',\n id: 'org.openedx.frontend.widget.subscriptionCheckout.header.mobileLogoLeft.v1',\n op: WidgetOperationTypes.APPEND,\n // checkout-specific mobile-left widget: shows paid logo after payment\n element: <MobileLeftPaidLogo />,\n condition: {\n active: [subscriptionCheckoutRole],\n },\n },\n];\n\nexport default slots;\n"]}
package/dist/types.d.ts CHANGED
@@ -153,10 +153,18 @@ export interface BillingInfo {
153
153
  line1: string;
154
154
  state: string;
155
155
  }
156
+ /** Result of tCartCancel (SUBS-1127). */
157
+ export interface CartCancelResult {
158
+ ok: boolean;
159
+ /** NOT_AUTHENTICATED | BI_ID_REQUIRED | CART_NOT_FOUND | NOT_YOUR_CART | CART_NOT_OPEN | CANCEL_FAILED */
160
+ error?: string;
161
+ }
156
162
  export interface CheckoutClient {
157
163
  gql: GqlRunner;
158
164
  setToken: (token: string) => void;
159
165
  createCart: (vars: CartIds) => Promise<Cart>;
166
+ /** Abandon the learner's own open cart, cancelling any pending Stripe / PayPal subscription on it. */
167
+ cancelCart: (biId: string) => Promise<CartCancelResult>;
160
168
  getCartItem: (biId: string) => Promise<{
161
169
  cartUid: string;
162
170
  cartItemUid: string;
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Status, Phase } from './constants';\n\nexport type { Status, Phase };\n\n// --- Bootstrap domain --------------------------------------------------------\nexport interface TwouCustomerContact {\n email?: string,\n}\n\nexport interface TwouCustomerParty {\n paName: string,\n indFirstName?: string,\n indLastName?: string,\n paContacts: { edges: { node: TwouCustomerContact }[] },\n}\n\nexport interface TwouCustomer {\n uid: string,\n parId: string,\n parParty: TwouCustomerParty,\n}\n\nexport type GqlRunner = (query: string, variables?: Record<string, unknown>, retry?: boolean) => Promise<any>;\n\n// --- Cart domain -------------------------------------------------------------\nexport interface Cart {\n biId: string,\n uid: string,\n}\n\nexport interface CartIds {\n customerUid: string,\n salableUid: string,\n billingPlanUid: string,\n}\n\nexport type BillingCycle = 'monthly' | 'annual';\n\nexport type PaymentMethod = 'card' | 'googlepay' | 'paypal';\n\nexport type SupportedPaymentMethod = Exclude<PaymentMethod, 'googlepay'>;\n\nexport type BillingPeriod = 'MONTHLY' | 'ANNUAL' | 'QUARTERLY' | string;\n\nexport interface BillingPlanOwnershipModel {\n uid: string,\n omName: string,\n}\n\nexport interface BillingPlan {\n uid: string,\n stripePriceId: string,\n /** Price amount e.g. \"39.99\" */\n amount: string,\n /** Currency code e.g. \"USD\" */\n currency: string,\n isTemplate: boolean,\n ownershipModel: BillingPlanOwnershipModel,\n}\n\nexport interface SubscriptionDetails {\n biId: string, // \"BI301ZDAD6CMICG2\"\n trialStart: string | null,\n trialEnd: string | null,\n renewalType: string | null, // \"Annual\" | \"Monthly\"\n cardLast4: string | null, // \"4242\"\n cardBrand: string | null, // \"visa\" | \"mastercard\" etc\n stripePaymentMethodId: string | null,\n planName: string | null, // \"Monthly Subscription\"\n}\n\nexport interface CurrentUserSubscriberResult {\n isSubscriber: boolean,\n subscriptionBiId: string | null,\n status: string | null,\n subscriptionStartDate: string | null,\n currentPeriodEnd: string | null,\n}\n\n/** Course shown on checkout / post-checkout when arriving via ?course_run_key=. */\nexport interface CourseDetail {\n maId: string,\n maName: string,\n maDescription: string | null,\n tShortDescription: string | null,\n imageUrl: string | null,\n tCourseKey: string | null,\n tBrandText: string | null,\n tPrimarySubjectArea: string | null,\n tLevelType: string | null,\n tLanguage: string | null,\n tPacingType: string | null,\n tDurationLow: number | null,\n tDurationHigh: number | null,\n tDurationUnit: string | null,\n tEffortLow: number | null,\n tEffortHigh: number | null,\n tEffortUnit: string | null,\n tUrlCourse: string | null,\n tB2cSubscriptionInclusion: boolean | null,\n tHasOfacRestrictions: boolean | null,\n tCatalogActive: boolean | null,\n}\n\n/** Returned by getBillingPlans — all available plans for the product. */\nexport interface BillingPlansResult {\n plans: BillingPlan[],\n}\n\n/** Returned by getSalableUid — the product uid and its ownership models. */\nexport interface ProductInfo {\n uid: string,\n maName: string,\n ownershipModels: { uid: string, omName: string }[],\n}\n\nexport interface CartStatusResult {\n status: Status | null,\n stripeSubscriptionId: string | null,\n /** Stripe-hosted challenge URL for the checkout confirmation step. */\n stripeConfirmationRedirectUrl: string | null,\n /** PayPal-hosted approval URL for the same checkout confirmation step. */\n paypalApprovalUrl: string | null,\n lastComment: string,\n}\n\nexport interface CartBillingAddress {\n tFirstName: string,\n tLastName: string,\n upaLine1: string,\n upaCity: string,\n upaPostcode: string,\n gadStateOrProvince: string,\n gadCountry: { iso2Code: string },\n}\n\nexport interface UpdateCartItemArgs {\n cartItemUid: string,\n billingAddress?: CartBillingAddress,\n paymentMethodId?: string,\n /** Payer email attached when the payment type is TwouPaypalPayment. */\n paypalEmail?: string | null,\n}\n\n/** Auto-renewal consent captured on TwouCart at checkout confirmation (SUBS-228). */\nexport interface CheckoutConsent {\n /** Main auto-renewal checkbox — always required at submit. */\n tConsentRecurring: boolean,\n /** UK/CA resident checkbox; null when that checkbox is not shown. */\n tConsentUkCa: boolean | null,\n /** ISO-8601 timestamp when consent was captured. */\n tConsentAt: string,\n /** Exact disclosure text shown to the learner (audit trail). */\n tConsentText: string,\n}\n\nexport interface UpdateCartConsentArgs {\n cartUid: string,\n consent: CheckoutConsent,\n /** Absolute checkout return URL stored in the backend's tStripeReturnUrl field. */\n returnUrl?: string | null,\n}\n\nexport interface BillingInfo {\n firstName: string,\n lastName: string,\n city: string,\n zip: string,\n country: string,\n line1: string,\n state: string,\n}\n\n// --- Checkout client surface -------------------------------------------------\nexport interface CheckoutClient {\n gql: GqlRunner,\n setToken: (token: string) => void,\n createCart: (vars: CartIds) => Promise<Cart>,\n getCartItem: (biId: string) => Promise<{ cartUid: string, cartItemUid: string }>,\n updateCartItem: (args: UpdateCartItemArgs) => Promise<void>,\n updateCartConsent: (args: UpdateCartConsentArgs) => Promise<void>,\n waitForCartBusinessProcess: (biId: string) => Promise<void>,\n placeOrder: (cartUid: string) => Promise<unknown>,\n getCartStatus: (biId: string) => Promise<CartStatusResult>,\n findSubscriptionBiId: (biId: string) => Promise<SubscriptionDetails | null>,\n getSalableUid: (maId: string) => Promise<ProductInfo>,\n getBillingPlans: () => Promise<BillingPlansResult>,\n getActiveSubscriptions: () => Promise<{ active: number }>,\n isCurrentUserSubscriber: () => Promise<CurrentUserSubscriberResult | null>,\n getCourseDetail: (courseKey: string) => Promise<CourseDetail | null>,\n}\n\nexport interface DevAuth {\n iamUrl: string,\n clientId: string,\n clientSecret: string,\n}\n\n// --- Minimal Stripe.js types -------------------------------------------------\n// Just the surface this module uses. If you depend on `@stripe/stripe-js`,\n// you can replace these with its richer types.\nexport interface StripeElementChangeEvent {\n error?: { message?: string },\n [key: string]: unknown,\n}\n\nexport interface StripeElementLike {\n mount: (node: HTMLElement) => void,\n on: (event: string, handler: (e: StripeElementChangeEvent) => void) => void,\n unmount?: () => void,\n}\n\nexport interface StripeElementsLike {\n create: (type: string, options?: Record<string, unknown>) => StripeElementLike,\n}\n\nexport interface StripeError {\n message?: string,\n code?: string,\n}\n\nexport interface PaymentMethodResult {\n paymentMethod?: { id: string },\n error?: StripeError,\n}\n\nexport interface ConfirmResult {\n error?: StripeError,\n setupIntent?: unknown,\n paymentIntent?: unknown,\n}\n\nexport interface CreatePaymentMethodParams {\n type: 'card',\n card: StripeElementLike,\n billing_details?: Record<string, unknown>,\n}\n\nexport interface StripeLike {\n elements: () => StripeElementsLike,\n createPaymentMethod: (params: CreatePaymentMethodParams) => Promise<PaymentMethodResult>,\n confirmCardSetup: (clientSecret: string) => Promise<ConfirmResult>,\n confirmCardPayment: (clientSecret: string) => Promise<ConfirmResult>,\n}\n\ndeclare global {\n interface Window {\n Stripe?: (publishableKey: string) => StripeLike,\n }\n}\n\nexport interface TrialDateRange {\n /** e.g. \"June 22, 2026\" */\n displayStart: string,\n /** e.g. \"June 29, 2026\" */\n displayEnd: string,\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Status, Phase } from './constants';\n\nexport type { Status, Phase };\n\n// --- Bootstrap domain --------------------------------------------------------\nexport interface TwouCustomerContact {\n email?: string,\n}\n\nexport interface TwouCustomerParty {\n paName: string,\n indFirstName?: string,\n indLastName?: string,\n paContacts: { edges: { node: TwouCustomerContact }[] },\n}\n\nexport interface TwouCustomer {\n uid: string,\n parId: string,\n parParty: TwouCustomerParty,\n}\n\nexport type GqlRunner = (query: string, variables?: Record<string, unknown>, retry?: boolean) => Promise<any>;\n\n// --- Cart domain -------------------------------------------------------------\nexport interface Cart {\n biId: string,\n uid: string,\n}\n\nexport interface CartIds {\n customerUid: string,\n salableUid: string,\n billingPlanUid: string,\n}\n\nexport type BillingCycle = 'monthly' | 'annual';\n\nexport type PaymentMethod = 'card' | 'googlepay' | 'paypal';\n\nexport type SupportedPaymentMethod = Exclude<PaymentMethod, 'googlepay'>;\n\nexport type BillingPeriod = 'MONTHLY' | 'ANNUAL' | 'QUARTERLY' | string;\n\nexport interface BillingPlanOwnershipModel {\n uid: string,\n omName: string,\n}\n\nexport interface BillingPlan {\n uid: string,\n stripePriceId: string,\n /** Price amount e.g. \"39.99\" */\n amount: string,\n /** Currency code e.g. \"USD\" */\n currency: string,\n isTemplate: boolean,\n ownershipModel: BillingPlanOwnershipModel,\n}\n\nexport interface SubscriptionDetails {\n biId: string, // \"BI301ZDAD6CMICG2\"\n trialStart: string | null,\n trialEnd: string | null,\n renewalType: string | null, // \"Annual\" | \"Monthly\"\n cardLast4: string | null, // \"4242\"\n cardBrand: string | null, // \"visa\" | \"mastercard\" etc\n stripePaymentMethodId: string | null,\n planName: string | null, // \"Monthly Subscription\"\n}\n\nexport interface CurrentUserSubscriberResult {\n isSubscriber: boolean,\n subscriptionBiId: string | null,\n status: string | null,\n subscriptionStartDate: string | null,\n currentPeriodEnd: string | null,\n}\n\n/** Course shown on checkout / post-checkout when arriving via ?course_run_key=. */\nexport interface CourseDetail {\n maId: string,\n maName: string,\n maDescription: string | null,\n tShortDescription: string | null,\n imageUrl: string | null,\n tCourseKey: string | null,\n tBrandText: string | null,\n tPrimarySubjectArea: string | null,\n tLevelType: string | null,\n tLanguage: string | null,\n tPacingType: string | null,\n tDurationLow: number | null,\n tDurationHigh: number | null,\n tDurationUnit: string | null,\n tEffortLow: number | null,\n tEffortHigh: number | null,\n tEffortUnit: string | null,\n tUrlCourse: string | null,\n tB2cSubscriptionInclusion: boolean | null,\n tHasOfacRestrictions: boolean | null,\n tCatalogActive: boolean | null,\n}\n\n/** Returned by getBillingPlans — all available plans for the product. */\nexport interface BillingPlansResult {\n plans: BillingPlan[],\n}\n\n/** Returned by getSalableUid — the product uid and its ownership models. */\nexport interface ProductInfo {\n uid: string,\n maName: string,\n ownershipModels: { uid: string, omName: string }[],\n}\n\nexport interface CartStatusResult {\n status: Status | null,\n stripeSubscriptionId: string | null,\n /** Stripe-hosted challenge URL for the checkout confirmation step. */\n stripeConfirmationRedirectUrl: string | null,\n /** PayPal-hosted approval URL for the same checkout confirmation step. */\n paypalApprovalUrl: string | null,\n lastComment: string,\n}\n\nexport interface CartBillingAddress {\n tFirstName: string,\n tLastName: string,\n upaLine1: string,\n upaCity: string,\n upaPostcode: string,\n gadStateOrProvince: string,\n gadCountry: { iso2Code: string },\n}\n\nexport interface UpdateCartItemArgs {\n cartItemUid: string,\n billingAddress?: CartBillingAddress,\n paymentMethodId?: string,\n /** Payer email attached when the payment type is TwouPaypalPayment. */\n paypalEmail?: string | null,\n}\n\n/** Auto-renewal consent captured on TwouCart at checkout confirmation (SUBS-228). */\nexport interface CheckoutConsent {\n /** Main auto-renewal checkbox — always required at submit. */\n tConsentRecurring: boolean,\n /** UK/CA resident checkbox; null when that checkbox is not shown. */\n tConsentUkCa: boolean | null,\n /** ISO-8601 timestamp when consent was captured. */\n tConsentAt: string,\n /** Exact disclosure text shown to the learner (audit trail). */\n tConsentText: string,\n}\n\nexport interface UpdateCartConsentArgs {\n cartUid: string,\n consent: CheckoutConsent,\n /** Absolute checkout return URL stored in the backend's tStripeReturnUrl field. */\n returnUrl?: string | null,\n}\n\nexport interface BillingInfo {\n firstName: string,\n lastName: string,\n city: string,\n zip: string,\n country: string,\n line1: string,\n state: string,\n}\n\n// --- Checkout client surface -------------------------------------------------\n/** Result of tCartCancel (SUBS-1127). */\nexport interface CartCancelResult {\n ok: boolean,\n /** NOT_AUTHENTICATED | BI_ID_REQUIRED | CART_NOT_FOUND | NOT_YOUR_CART | CART_NOT_OPEN | CANCEL_FAILED */\n error?: string,\n}\n\nexport interface CheckoutClient {\n gql: GqlRunner,\n setToken: (token: string) => void,\n createCart: (vars: CartIds) => Promise<Cart>,\n /** Abandon the learner's own open cart, cancelling any pending Stripe / PayPal subscription on it. */\n cancelCart: (biId: string) => Promise<CartCancelResult>,\n getCartItem: (biId: string) => Promise<{ cartUid: string, cartItemUid: string }>,\n updateCartItem: (args: UpdateCartItemArgs) => Promise<void>,\n updateCartConsent: (args: UpdateCartConsentArgs) => Promise<void>,\n waitForCartBusinessProcess: (biId: string) => Promise<void>,\n placeOrder: (cartUid: string) => Promise<unknown>,\n getCartStatus: (biId: string) => Promise<CartStatusResult>,\n findSubscriptionBiId: (biId: string) => Promise<SubscriptionDetails | null>,\n getSalableUid: (maId: string) => Promise<ProductInfo>,\n getBillingPlans: () => Promise<BillingPlansResult>,\n getActiveSubscriptions: () => Promise<{ active: number }>,\n isCurrentUserSubscriber: () => Promise<CurrentUserSubscriberResult | null>,\n getCourseDetail: (courseKey: string) => Promise<CourseDetail | null>,\n}\n\nexport interface DevAuth {\n iamUrl: string,\n clientId: string,\n clientSecret: string,\n}\n\n// --- Minimal Stripe.js types -------------------------------------------------\n// Just the surface this module uses. If you depend on `@stripe/stripe-js`,\n// you can replace these with its richer types.\nexport interface StripeElementChangeEvent {\n error?: { message?: string },\n [key: string]: unknown,\n}\n\nexport interface StripeElementLike {\n mount: (node: HTMLElement) => void,\n on: (event: string, handler: (e: StripeElementChangeEvent) => void) => void,\n unmount?: () => void,\n}\n\nexport interface StripeElementsLike {\n create: (type: string, options?: Record<string, unknown>) => StripeElementLike,\n}\n\nexport interface StripeError {\n message?: string,\n code?: string,\n}\n\nexport interface PaymentMethodResult {\n paymentMethod?: { id: string },\n error?: StripeError,\n}\n\nexport interface ConfirmResult {\n error?: StripeError,\n setupIntent?: unknown,\n paymentIntent?: unknown,\n}\n\nexport interface CreatePaymentMethodParams {\n type: 'card',\n card: StripeElementLike,\n billing_details?: Record<string, unknown>,\n}\n\nexport interface StripeLike {\n elements: () => StripeElementsLike,\n createPaymentMethod: (params: CreatePaymentMethodParams) => Promise<PaymentMethodResult>,\n confirmCardSetup: (clientSecret: string) => Promise<ConfirmResult>,\n confirmCardPayment: (clientSecret: string) => Promise<ConfirmResult>,\n}\n\ndeclare global {\n interface Window {\n Stripe?: (publishableKey: string) => StripeLike,\n }\n}\n\nexport interface TrialDateRange {\n /** e.g. \"June 22, 2026\" */\n displayStart: string,\n /** e.g. \"June 29, 2026\" */\n displayEnd: string,\n}\n"]}
@@ -12,6 +12,7 @@ export function createCheckoutClient({ apiUrl }) {
12
12
  gql,
13
13
  setToken,
14
14
  createCart: (vars) => cart.createCart(gql, vars),
15
+ cancelCart: (biId) => cart.cancelCart(gql, biId),
15
16
  getCartItem: (biId) => cart.getCartItem(gql, biId),
16
17
  updateCartItem: (args) => cart.updateCartItem(gql, args),
17
18
  updateCartConsent: (args) => cart.updateCartConsent(gql, args),
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/utils/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAU5C,MAAM,UAAU,oBAAoB,CAClC,EAAE,MAAM,EAAwB;IAEhC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAEpE,OAAO;QACL,GAAG;QACH,QAAQ;QACR,UAAU,EAAE,CAAC,IAAa,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;QACzD,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC;QAClD,cAAc,EAAE,CAAC,IAAwB,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC;QAC5E,iBAAiB,EAAE,CAAC,IAA2B,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC;QACrF,0BAA0B,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,GAAG,EAAE,IAAI,CAAC;QAChF,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;QACtD,aAAa,EAAE,WAAW;QAC1B,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC;QACpE,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC;QAC3D,eAAe,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC;QACrD,sBAAsB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,sBAAsB,CAAC,GAAG,CAAC;QACnE,uBAAuB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,uBAAuB,CAAC,GAAG,CAAC;QACrE,eAAe,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1E,CAAC;AACJ,CAAC","sourcesContent":["/**\n * api/index.ts — composes the pieces into a single checkout client and\n * re-exports the API utilities.\n */\nimport { createGqlClient } from './graphqlClient';\nimport * as cart from './cartApi';\nimport * as bootstrap from './bootstrapApi';\nimport type {\n CheckoutClient, CartIds, UpdateCartItemArgs, UpdateCartConsentArgs,\n} from '../types';\n\ninterface CheckoutClientConfig {\n apiUrl: string,\n confirmIntentUrl?: string,\n}\n\nexport function createCheckoutClient(\n { apiUrl }: CheckoutClientConfig,\n): CheckoutClient {\n const { gql, setToken } = createGqlClient({ apiUrl });\n const fetchStatus = (biId: string) => cart.getCartStatus(gql, biId);\n\n return {\n gql,\n setToken,\n createCart: (vars: CartIds) => cart.createCart(gql, vars),\n getCartItem: (biId) => cart.getCartItem(gql, biId),\n updateCartItem: (args: UpdateCartItemArgs) => cart.updateCartItem(gql, args),\n updateCartConsent: (args: UpdateCartConsentArgs) => cart.updateCartConsent(gql, args),\n waitForCartBusinessProcess: (biId) => cart.waitForCartBusinessProcess(gql, biId),\n placeOrder: (cartUid) => cart.placeOrder(gql, cartUid),\n getCartStatus: fetchStatus,\n findSubscriptionBiId: (biId) => cart.findSubscriptionBiId(gql, biId),\n getSalableUid: (maId) => bootstrap.getSalableUid(gql, maId),\n getBillingPlans: () => bootstrap.getBillingPlans(gql),\n getActiveSubscriptions: () => bootstrap.getActiveSubscriptions(gql),\n isCurrentUserSubscriber: () => bootstrap.isCurrentUserSubscriber(gql),\n getCourseDetail: (courseKey) => bootstrap.getCourseDetail(gql, courseKey),\n };\n}\n"]}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/utils/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAU5C,MAAM,UAAU,oBAAoB,CAClC,EAAE,MAAM,EAAwB;IAEhC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAEpE,OAAO;QACL,GAAG;QACH,QAAQ;QACR,UAAU,EAAE,CAAC,IAAa,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;QACzD,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;QAChD,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC;QAClD,cAAc,EAAE,CAAC,IAAwB,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC;QAC5E,iBAAiB,EAAE,CAAC,IAA2B,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC;QACrF,0BAA0B,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,GAAG,EAAE,IAAI,CAAC;QAChF,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;QACtD,aAAa,EAAE,WAAW;QAC1B,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC;QACpE,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC;QAC3D,eAAe,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC;QACrD,sBAAsB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,sBAAsB,CAAC,GAAG,CAAC;QACnE,uBAAuB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,uBAAuB,CAAC,GAAG,CAAC;QACrE,eAAe,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1E,CAAC;AACJ,CAAC","sourcesContent":["/**\n * api/index.ts — composes the pieces into a single checkout client and\n * re-exports the API utilities.\n */\nimport { createGqlClient } from './graphqlClient';\nimport * as cart from './cartApi';\nimport * as bootstrap from './bootstrapApi';\nimport type {\n CheckoutClient, CartIds, UpdateCartItemArgs, UpdateCartConsentArgs,\n} from '../types';\n\ninterface CheckoutClientConfig {\n apiUrl: string,\n confirmIntentUrl?: string,\n}\n\nexport function createCheckoutClient(\n { apiUrl }: CheckoutClientConfig,\n): CheckoutClient {\n const { gql, setToken } = createGqlClient({ apiUrl });\n const fetchStatus = (biId: string) => cart.getCartStatus(gql, biId);\n\n return {\n gql,\n setToken,\n createCart: (vars: CartIds) => cart.createCart(gql, vars),\n cancelCart: (biId) => cart.cancelCart(gql, biId),\n getCartItem: (biId) => cart.getCartItem(gql, biId),\n updateCartItem: (args: UpdateCartItemArgs) => cart.updateCartItem(gql, args),\n updateCartConsent: (args: UpdateCartConsentArgs) => cart.updateCartConsent(gql, args),\n waitForCartBusinessProcess: (biId) => cart.waitForCartBusinessProcess(gql, biId),\n placeOrder: (cartUid) => cart.placeOrder(gql, cartUid),\n getCartStatus: fetchStatus,\n findSubscriptionBiId: (biId) => cart.findSubscriptionBiId(gql, biId),\n getSalableUid: (maId) => bootstrap.getSalableUid(gql, maId),\n getBillingPlans: () => bootstrap.getBillingPlans(gql),\n getActiveSubscriptions: () => bootstrap.getActiveSubscriptions(gql),\n isCurrentUserSubscriber: () => bootstrap.isCurrentUserSubscriber(gql),\n getCourseDetail: (courseKey) => bootstrap.getCourseDetail(gql, courseKey),\n };\n}\n"]}
@@ -1,6 +1,8 @@
1
- import type { GqlRunner, Cart, CartIds, CartStatusResult, UpdateCartItemArgs, UpdateCartConsentArgs, SubscriptionDetails } from '../types';
1
+ import type { GqlRunner, Cart, CartIds, CartStatusResult, UpdateCartItemArgs, UpdateCartConsentArgs, SubscriptionDetails, CartCancelResult } from '../types';
2
2
  /** createTwouCart → { biId, uid } */
3
3
  export declare function createCart(gql: GqlRunner, { customerUid, salableUid, billingPlanUid }: CartIds): Promise<Cart>;
4
+ /** tCartCancel → { ok, error? }. A missing or malformed result counts as not cancelled. */
5
+ export declare function cancelCart(gql: GqlRunner, biId: string): Promise<CartCancelResult>;
4
6
  /** → { cartUid, cartItemUid } */
5
7
  export declare function getCartItem(gql: GqlRunner, biId: string): Promise<{
6
8
  cartUid: string;
@@ -12,7 +12,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
12
12
  * Each takes the `gql` runner as its first argument so they stay pure and
13
13
  * independently testable (mock `gql`, assert the variables).
14
14
  */
15
- import { CREATE_CART, GET_CART_ITEM, UPDATE_CART_ITEM, UPDATE_CART, PLACE_ORDER, GET_CART_BUSINESS_PROCESS, GET_CART_STATUS, FIND_SUBSCRIPTION, } from './queries';
15
+ import { CREATE_CART, GET_CART_ITEM, UPDATE_CART_ITEM, UPDATE_CART, PLACE_ORDER, GET_CART_BUSINESS_PROCESS, GET_CART_STATUS, FIND_SUBSCRIPTION, CANCEL_CART, } from './queries';
16
16
  /** createTwouCart → { biId, uid } */
17
17
  export function createCart(gql_1, _a) {
18
18
  return __awaiter(this, arguments, void 0, function* (gql, { customerUid, salableUid, billingPlanUid }) {
@@ -25,6 +25,17 @@ export function createCart(gql_1, _a) {
25
25
  return data.createTwouCart;
26
26
  });
27
27
  }
28
+ /** tCartCancel → { ok, error? }. A missing or malformed result counts as not cancelled. */
29
+ export function cancelCart(gql, biId) {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ var _a;
32
+ const data = yield gql(CANCEL_CART, { biId });
33
+ const result = data === null || data === void 0 ? void 0 : data.tCartCancel;
34
+ if (!result || typeof result !== 'object')
35
+ return { ok: false, error: 'CANCEL_FAILED' };
36
+ return { ok: result.ok === true, error: (_a = result.error) !== null && _a !== void 0 ? _a : undefined };
37
+ });
38
+ }
28
39
  /** → { cartUid, cartItemUid } */
29
40
  export function getCartItem(gql, biId) {
30
41
  return __awaiter(this, void 0, void 0, function* () {
@@ -1 +1 @@
1
- {"version":3,"file":"cartApi.js","sourceRoot":"","sources":["../../src/utils/cartApi.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api/cartApi.ts — one function per checkout operation.\n * Each takes the `gql` runner as its first argument so they stay pure and\n * independently testable (mock `gql`, assert the variables).\n */\nimport {\n CREATE_CART, GET_CART_ITEM, UPDATE_CART_ITEM, UPDATE_CART,\n PLACE_ORDER, GET_CART_BUSINESS_PROCESS, GET_CART_STATUS, FIND_SUBSCRIPTION,\n} from './queries';\nimport type {\n GqlRunner, Cart, CartIds, CartStatusResult, UpdateCartItemArgs,\n UpdateCartConsentArgs, Status, SubscriptionDetails,\n} from '../types';\n\n/** createTwouCart → { biId, uid } */\nexport async function createCart(gql: GqlRunner, { customerUid, salableUid, billingPlanUid }: CartIds): Promise<Cart> {\n const data = await gql(CREATE_CART, {\n input: {\n prPartyPrimary: { uid: customerUid },\n hiConsistsOf: [{ biiSalable: { uid: salableUid }, paybBillingPlan: { uid: billingPlanUid } }],\n },\n });\n return data.createTwouCart as Cart;\n}\n\n/** → { cartUid, cartItemUid } */\nexport async function getCartItem(gql: GqlRunner, biId: string): Promise<{ cartUid: string, cartItemUid: string }> {\n const data = await gql(GET_CART_ITEM, { biId });\n const bi = data.getBusinessInteraction;\n const cartUid = bi?.uid as string | undefined;\n const cartItemUid = bi?.hiConsistsOf?.[0]?.uid as string | undefined;\n\n if (!cartUid || !cartItemUid) {\n throw new Error(`Cart item not found for cart \"${biId}\".`);\n }\n\n return { cartUid, cartItemUid };\n}\n\nexport async function updateCartItem(\n gql: GqlRunner,\n { cartItemUid, billingAddress, paymentMethodId, paypalEmail }: UpdateCartItemArgs,\n): Promise<void> {\n const input: Record<string, unknown> = { uid: cartItemUid };\n // Interface-typed inputs accept exactly one key, named after the concrete\n // type, and all of its fields live inside it.\n if (billingAddress) input.paybBillingAddress = { _viaxUrbanPropertyAddress: { ...billingAddress } };\n if (paymentMethodId) {\n input.paybPayment = {\n _twouStripePayment: { tStripePaymentMethodId: paymentMethodId },\n };\n } else if (paypalEmail !== undefined) {\n input.paybPayment = {\n _twouPaypalPayment: paypalEmail ? { tPaypalEmail: paypalEmail } : {},\n };\n }\n await gql(UPDATE_CART_ITEM, { input });\n}\n\n/** Persist checkout consent checkboxes and the hosted-payment return URL onto the TwouCart BI. */\nexport async function updateCartConsent(\n gql: GqlRunner,\n { cartUid, consent, returnUrl }: UpdateCartConsentArgs,\n): Promise<void> {\n const input: Record<string, unknown> = {\n uid: cartUid,\n tConsentRecurring: consent.tConsentRecurring,\n tConsentUkCa: consent.tConsentUkCa,\n tConsentAt: consent.tConsentAt,\n tConsentText: consent.tConsentText,\n };\n if (returnUrl) input.tStripeReturnUrl = returnUrl;\n await gql(UPDATE_CART, { input });\n}\n\nconst CART_WORKFLOW_POLL_INTERVAL_MS = 250;\nconst CART_WORKFLOW_MAX_ATTEMPTS = 20;\n\ninterface WaitForCartBusinessProcessOptions {\n intervalMs?: number,\n maxAttempts?: number,\n}\n\n/** Wait until the asynchronously-created workflow is attached to the cart. */\nexport async function waitForCartBusinessProcess(\n gql: GqlRunner,\n biId: string,\n {\n intervalMs = CART_WORKFLOW_POLL_INTERVAL_MS,\n maxAttempts = CART_WORKFLOW_MAX_ATTEMPTS,\n }: WaitForCartBusinessProcessOptions = {},\n): Promise<void> {\n for (let attempt = 0; attempt < maxAttempts; attempt += 1) {\n const data = await gql(GET_CART_BUSINESS_PROCESS, { biId });\n if (data?.getBusinessInteraction?.bpBusinessProcess != null) return;\n\n if (attempt < maxAttempts - 1) {\n await new Promise(resolve => setTimeout(resolve, intervalMs));\n }\n }\n\n throw new Error('The cart is still being prepared. Please try placing your order again.');\n}\n\n/** \"Place order\" — triggers the server-side workflow. */\nexport async function placeOrder(gql: GqlRunner, cartUid: string): Promise<unknown> {\n const data = await gql(PLACE_ORDER, {\n input: {\n businessProcessable: { uid: cartUid },\n transitionCode: 'Sanction_Check',\n },\n });\n return data.startBusinessProcessableTransitionV2;\n}\n\n/** → current cart workflow status, Stripe details, and latest comment. */\nexport async function getCartStatus(gql: GqlRunner, biId: string): Promise<CartStatusResult> {\n const data = await gql(GET_CART_STATUS, { biId });\n const bi = data?.getBusinessInteraction || {};\n const comments: string[] = bi.cmComments?.edges?.map((e: { node: { comText: string } }) => e.node.comText) || [];\n return {\n status: (bi.bpStatus?.code ?? null) as Status | null,\n stripeSubscriptionId: bi.hiConsistsOf?.[0]?.tStripeSubscriptionId ?? null,\n stripeConfirmationRedirectUrl: bi.tStripeConfirmationRedirectUrl ?? null,\n paypalApprovalUrl: bi.tPaypalApprovalUrl ?? null,\n lastComment: comments.length ? comments[comments.length - 1] : '',\n };\n}\n\nexport async function findSubscriptionBiId(gql: GqlRunner, cartBiId: string): Promise<SubscriptionDetails | null> {\n try {\n const data = await gql(FIND_SUBSCRIPTION, { filter: `biRelatesTo.biId = '${cartBiId}'` });\n const node = data?.filterTwouSubscription?.edges?.[0]?.node;\n if (!node?.biId) return null;\n\n const item = node.hiConsistsOf?.[0];\n const payment = item?.paybPayment;\n\n return {\n biId: node.biId,\n // TwouSubscription exposes tTrialGranted / tTrialPeriod, not start/end dates.\n // Trial display dates are computed client-side via getTrialDateRange().\n trialStart: null,\n trialEnd: null,\n renewalType: node.tRenewalType ?? null,\n cardLast4: payment?.tPaymentMethodLast4 ?? null, // ← last four digits\n cardBrand: payment?.tPaymentMethodBrand ?? null, // ← card type (Visa, Mastercard etc)\n stripePaymentMethodId: payment?.tStripePaymentMethodId ?? null,\n planName: item?.paybBillingPlan?.bipOwnershipModel?.omName ?? null,\n };\n } catch {\n return null;\n }\n}\n"]}
1
+ {"version":3,"file":"cartApi.js","sourceRoot":"","sources":["../../src/utils/cartApi.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api/cartApi.ts — one function per checkout operation.\n * Each takes the `gql` runner as its first argument so they stay pure and\n * independently testable (mock `gql`, assert the variables).\n */\nimport {\n CREATE_CART, GET_CART_ITEM, UPDATE_CART_ITEM, UPDATE_CART,\n PLACE_ORDER, GET_CART_BUSINESS_PROCESS, GET_CART_STATUS, FIND_SUBSCRIPTION, CANCEL_CART,\n} from './queries';\nimport type {\n GqlRunner, Cart, CartIds, CartStatusResult, UpdateCartItemArgs,\n UpdateCartConsentArgs, Status, SubscriptionDetails, CartCancelResult,\n} from '../types';\n\n/** createTwouCart → { biId, uid } */\nexport async function createCart(gql: GqlRunner, { customerUid, salableUid, billingPlanUid }: CartIds): Promise<Cart> {\n const data = await gql(CREATE_CART, {\n input: {\n prPartyPrimary: { uid: customerUid },\n hiConsistsOf: [{ biiSalable: { uid: salableUid }, paybBillingPlan: { uid: billingPlanUid } }],\n },\n });\n return data.createTwouCart as Cart;\n}\n\n/** tCartCancel → { ok, error? }. A missing or malformed result counts as not cancelled. */\nexport async function cancelCart(gql: GqlRunner, biId: string): Promise<CartCancelResult> {\n const data = await gql(CANCEL_CART, { biId });\n const result = data?.tCartCancel;\n if (!result || typeof result !== 'object') return { ok: false, error: 'CANCEL_FAILED' };\n return { ok: result.ok === true, error: result.error ?? undefined };\n}\n\n/** → { cartUid, cartItemUid } */\nexport async function getCartItem(gql: GqlRunner, biId: string): Promise<{ cartUid: string, cartItemUid: string }> {\n const data = await gql(GET_CART_ITEM, { biId });\n const bi = data.getBusinessInteraction;\n const cartUid = bi?.uid as string | undefined;\n const cartItemUid = bi?.hiConsistsOf?.[0]?.uid as string | undefined;\n\n if (!cartUid || !cartItemUid) {\n throw new Error(`Cart item not found for cart \"${biId}\".`);\n }\n\n return { cartUid, cartItemUid };\n}\n\nexport async function updateCartItem(\n gql: GqlRunner,\n { cartItemUid, billingAddress, paymentMethodId, paypalEmail }: UpdateCartItemArgs,\n): Promise<void> {\n const input: Record<string, unknown> = { uid: cartItemUid };\n // Interface-typed inputs accept exactly one key, named after the concrete\n // type, and all of its fields live inside it.\n if (billingAddress) input.paybBillingAddress = { _viaxUrbanPropertyAddress: { ...billingAddress } };\n if (paymentMethodId) {\n input.paybPayment = {\n _twouStripePayment: { tStripePaymentMethodId: paymentMethodId },\n };\n } else if (paypalEmail !== undefined) {\n input.paybPayment = {\n _twouPaypalPayment: paypalEmail ? { tPaypalEmail: paypalEmail } : {},\n };\n }\n await gql(UPDATE_CART_ITEM, { input });\n}\n\n/** Persist checkout consent checkboxes and the hosted-payment return URL onto the TwouCart BI. */\nexport async function updateCartConsent(\n gql: GqlRunner,\n { cartUid, consent, returnUrl }: UpdateCartConsentArgs,\n): Promise<void> {\n const input: Record<string, unknown> = {\n uid: cartUid,\n tConsentRecurring: consent.tConsentRecurring,\n tConsentUkCa: consent.tConsentUkCa,\n tConsentAt: consent.tConsentAt,\n tConsentText: consent.tConsentText,\n };\n if (returnUrl) input.tStripeReturnUrl = returnUrl;\n await gql(UPDATE_CART, { input });\n}\n\nconst CART_WORKFLOW_POLL_INTERVAL_MS = 250;\nconst CART_WORKFLOW_MAX_ATTEMPTS = 20;\n\ninterface WaitForCartBusinessProcessOptions {\n intervalMs?: number,\n maxAttempts?: number,\n}\n\n/** Wait until the asynchronously-created workflow is attached to the cart. */\nexport async function waitForCartBusinessProcess(\n gql: GqlRunner,\n biId: string,\n {\n intervalMs = CART_WORKFLOW_POLL_INTERVAL_MS,\n maxAttempts = CART_WORKFLOW_MAX_ATTEMPTS,\n }: WaitForCartBusinessProcessOptions = {},\n): Promise<void> {\n for (let attempt = 0; attempt < maxAttempts; attempt += 1) {\n const data = await gql(GET_CART_BUSINESS_PROCESS, { biId });\n if (data?.getBusinessInteraction?.bpBusinessProcess != null) return;\n\n if (attempt < maxAttempts - 1) {\n await new Promise(resolve => setTimeout(resolve, intervalMs));\n }\n }\n\n throw new Error('The cart is still being prepared. Please try placing your order again.');\n}\n\n/** \"Place order\" — triggers the server-side workflow. */\nexport async function placeOrder(gql: GqlRunner, cartUid: string): Promise<unknown> {\n const data = await gql(PLACE_ORDER, {\n input: {\n businessProcessable: { uid: cartUid },\n transitionCode: 'Sanction_Check',\n },\n });\n return data.startBusinessProcessableTransitionV2;\n}\n\n/** → current cart workflow status, Stripe details, and latest comment. */\nexport async function getCartStatus(gql: GqlRunner, biId: string): Promise<CartStatusResult> {\n const data = await gql(GET_CART_STATUS, { biId });\n const bi = data?.getBusinessInteraction || {};\n const comments: string[] = bi.cmComments?.edges?.map((e: { node: { comText: string } }) => e.node.comText) || [];\n return {\n status: (bi.bpStatus?.code ?? null) as Status | null,\n stripeSubscriptionId: bi.hiConsistsOf?.[0]?.tStripeSubscriptionId ?? null,\n stripeConfirmationRedirectUrl: bi.tStripeConfirmationRedirectUrl ?? null,\n paypalApprovalUrl: bi.tPaypalApprovalUrl ?? null,\n lastComment: comments.length ? comments[comments.length - 1] : '',\n };\n}\n\nexport async function findSubscriptionBiId(gql: GqlRunner, cartBiId: string): Promise<SubscriptionDetails | null> {\n try {\n const data = await gql(FIND_SUBSCRIPTION, { filter: `biRelatesTo.biId = '${cartBiId}'` });\n const node = data?.filterTwouSubscription?.edges?.[0]?.node;\n if (!node?.biId) return null;\n\n const item = node.hiConsistsOf?.[0];\n const payment = item?.paybPayment;\n\n return {\n biId: node.biId,\n // TwouSubscription exposes tTrialGranted / tTrialPeriod, not start/end dates.\n // Trial display dates are computed client-side via getTrialDateRange().\n trialStart: null,\n trialEnd: null,\n renewalType: node.tRenewalType ?? null,\n cardLast4: payment?.tPaymentMethodLast4 ?? null, // ← last four digits\n cardBrand: payment?.tPaymentMethodBrand ?? null, // ← card type (Visa, Mastercard etc)\n stripePaymentMethodId: payment?.tStripePaymentMethodId ?? null,\n planName: item?.paybBillingPlan?.bipOwnershipModel?.omName ?? null,\n };\n } catch {\n return null;\n }\n}\n"]}
@@ -0,0 +1,16 @@
1
+ export type CartRefusalCode = 'OPEN_CART_EXISTS' | 'ACTIVE_SUBSCRIPTION';
2
+ export interface CartRefusal {
3
+ code: CartRefusalCode;
4
+ biId: string;
5
+ }
6
+ /** The refusal behind a failed createCart, or null when the error is something else. */
7
+ export declare function parseCartRefusal(err: unknown): CartRefusal | null;
8
+ /**
9
+ * Open-cart states whose payment is still being worked on. Cancelling one of
10
+ * these could orphan a subscription that is half-made or already approved, so
11
+ * the learner waits instead; the stale-cart job abandons it if it never finishes.
12
+ */
13
+ export declare const CART_IN_PROGRESS_STATES: readonly string[];
14
+ export declare const ALREADY_SUBSCRIBED_MESSAGE = "You already have an active subscription. Go to Manage subscription to view it.";
15
+ export declare const PREVIOUS_ORDER_PROCESSING_MESSAGE = "Your previous order is still being processed. Please wait a few minutes and try again.";
16
+ export declare const PREVIOUS_ORDER_STUCK_MESSAGE = "We could not clear your previous checkout. Please refresh the page and try again.";
@@ -0,0 +1,47 @@
1
+ /**
2
+ * utils/cartRefusal.ts — read why createTwouCart was refused (SUBS-1127).
3
+ *
4
+ * The backend allows one open subscription cart per learner. A refused
5
+ * create comes back as the generic "Validation check is failed" message, with
6
+ * the reason as `<CODE>:<biId>` inside the error's `extensions`:
7
+ * OPEN_CART_EXISTS:<cart biId> — an earlier cart is still open
8
+ * ACTIVE_SUBSCRIPTION:<subscription biId> — the learner already subscribes
9
+ * The code is searched for anywhere in the error, so a change in where the
10
+ * platform nests it does not break the lookup.
11
+ */
12
+ import { GqlError } from './graphqlClient';
13
+ const REFUSAL_PATTERN = /(OPEN_CART_EXISTS|ACTIVE_SUBSCRIPTION):([A-Za-z0-9_-]+)/;
14
+ /** The refusal behind a failed createCart, or null when the error is something else. */
15
+ export function parseCartRefusal(err) {
16
+ if (!(err instanceof Error))
17
+ return null;
18
+ let text = err.message;
19
+ if (err instanceof GqlError) {
20
+ try {
21
+ text += ` ${JSON.stringify(err.errors)}`;
22
+ }
23
+ catch (_a) {
24
+ // Unserialisable extensions — the message alone is all there is to search.
25
+ }
26
+ }
27
+ const match = REFUSAL_PATTERN.exec(text);
28
+ return match ? { code: match[1], biId: match[2] } : null;
29
+ }
30
+ /**
31
+ * Open-cart states whose payment is still being worked on. Cancelling one of
32
+ * these could orphan a subscription that is half-made or already approved, so
33
+ * the learner waits instead; the stale-cart job abandons it if it never finishes.
34
+ */
35
+ export const CART_IN_PROGRESS_STATES = [
36
+ 'CONFIRM',
37
+ 'SANCTION_STARTED',
38
+ 'SANCTION_PASSED',
39
+ 'STRIPE_STARTED',
40
+ 'STRIPE_SUBSCRIBED',
41
+ 'PAYPAL_STARTED',
42
+ 'PAYPAL_SUBSCRIBED',
43
+ ];
44
+ export const ALREADY_SUBSCRIBED_MESSAGE = 'You already have an active subscription. Go to Manage subscription to view it.';
45
+ export const PREVIOUS_ORDER_PROCESSING_MESSAGE = 'Your previous order is still being processed. Please wait a few minutes and try again.';
46
+ export const PREVIOUS_ORDER_STUCK_MESSAGE = 'We could not clear your previous checkout. Please refresh the page and try again.';
47
+ //# sourceMappingURL=cartRefusal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cartRefusal.js","sourceRoot":"","sources":["../../src/utils/cartRefusal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAS3C,MAAM,eAAe,GAAG,yDAAyD,CAAC;AAElF,wFAAwF;AACxF,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC;IACvB,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,IAAI,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3C,CAAC;QAAC,WAAM,CAAC;YACP,2EAA2E;QAC7E,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAoB,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAsB;IACxD,SAAS;IACT,kBAAkB;IAClB,iBAAiB;IACjB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,mBAAmB;CACpB,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG,gFAAgF,CAAC;AAC3H,MAAM,CAAC,MAAM,iCAAiC,GAAG,wFAAwF,CAAC;AAC1I,MAAM,CAAC,MAAM,4BAA4B,GAAG,mFAAmF,CAAC","sourcesContent":["/**\n * utils/cartRefusal.ts — read why createTwouCart was refused (SUBS-1127).\n *\n * The backend allows one open subscription cart per learner. A refused\n * create comes back as the generic \"Validation check is failed\" message, with\n * the reason as `<CODE>:<biId>` inside the error's `extensions`:\n * OPEN_CART_EXISTS:<cart biId> — an earlier cart is still open\n * ACTIVE_SUBSCRIPTION:<subscription biId> — the learner already subscribes\n * The code is searched for anywhere in the error, so a change in where the\n * platform nests it does not break the lookup.\n */\nimport { GqlError } from './graphqlClient';\n\nexport type CartRefusalCode = 'OPEN_CART_EXISTS' | 'ACTIVE_SUBSCRIPTION';\n\nexport interface CartRefusal {\n code: CartRefusalCode,\n biId: string,\n}\n\nconst REFUSAL_PATTERN = /(OPEN_CART_EXISTS|ACTIVE_SUBSCRIPTION):([A-Za-z0-9_-]+)/;\n\n/** The refusal behind a failed createCart, or null when the error is something else. */\nexport function parseCartRefusal(err: unknown): CartRefusal | null {\n if (!(err instanceof Error)) return null;\n let text = err.message;\n if (err instanceof GqlError) {\n try {\n text += ` ${JSON.stringify(err.errors)}`;\n } catch {\n // Unserialisable extensions — the message alone is all there is to search.\n }\n }\n const match = REFUSAL_PATTERN.exec(text);\n return match ? { code: match[1] as CartRefusalCode, biId: match[2] } : null;\n}\n\n/**\n * Open-cart states whose payment is still being worked on. Cancelling one of\n * these could orphan a subscription that is half-made or already approved, so\n * the learner waits instead; the stale-cart job abandons it if it never finishes.\n */\nexport const CART_IN_PROGRESS_STATES: readonly string[] = [\n 'CONFIRM',\n 'SANCTION_STARTED',\n 'SANCTION_PASSED',\n 'STRIPE_STARTED',\n 'STRIPE_SUBSCRIBED',\n 'PAYPAL_STARTED',\n 'PAYPAL_SUBSCRIBED',\n];\n\nexport const ALREADY_SUBSCRIBED_MESSAGE = 'You already have an active subscription. Go to Manage subscription to view it.';\nexport const PREVIOUS_ORDER_PROCESSING_MESSAGE = 'Your previous order is still being processed. Please wait a few minutes and try again.';\nexport const PREVIOUS_ORDER_STUCK_MESSAGE = 'We could not clear your previous checkout. Please refresh the page and try again.';\n"]}
@@ -12,6 +12,7 @@ export function createCheckoutClient({ apiUrl }) {
12
12
  gql,
13
13
  setToken,
14
14
  createCart: (vars) => cart.createCart(gql, vars),
15
+ cancelCart: (biId) => cart.cancelCart(gql, biId),
15
16
  getCartItem: (biId) => cart.getCartItem(gql, biId),
16
17
  updateCartItem: (args) => cart.updateCartItem(gql, args),
17
18
  updateCartConsent: (args) => cart.updateCartConsent(gql, args),
@@ -1 +1 @@
1
- {"version":3,"file":"checkout.js","sourceRoot":"","sources":["../../src/utils/checkout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAS5C,MAAM,UAAU,oBAAoB,CAClC,EAAE,MAAM,EAAwB;IAEhC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAEpE,OAAO;QACL,GAAG;QACH,QAAQ;QACR,UAAU,EAAE,CAAC,IAAa,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;QACzD,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC;QAClD,cAAc,EAAE,CAAC,IAAwB,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC;QAC5E,iBAAiB,EAAE,CAAC,IAA2B,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC;QACrF,0BAA0B,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,GAAG,EAAE,IAAI,CAAC;QAChF,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;QACtD,aAAa,EAAE,WAAW;QAC1B,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC;QACpE,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC;QAC3D,eAAe,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC;QACrD,sBAAsB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,sBAAsB,CAAC,GAAG,CAAC;QACnE,uBAAuB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,uBAAuB,CAAC,GAAG,CAAC;QACrE,eAAe,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1E,CAAC;AACJ,CAAC","sourcesContent":["/**\n * api/index.ts — composes the pieces into a single checkout client and\n * re-exports the API utilities.\n */\nimport { createGqlClient } from './graphqlClient';\nimport * as cart from './cartApi';\nimport * as bootstrap from './bootstrapApi';\nimport type {\n CheckoutClient, CartIds, UpdateCartItemArgs, UpdateCartConsentArgs,\n} from '../types';\n\ninterface CheckoutClientConfig {\n apiUrl: string,\n}\n\nexport function createCheckoutClient(\n { apiUrl }: CheckoutClientConfig,\n): CheckoutClient {\n const { gql, setToken } = createGqlClient({ apiUrl });\n const fetchStatus = (biId: string) => cart.getCartStatus(gql, biId);\n\n return {\n gql,\n setToken,\n createCart: (vars: CartIds) => cart.createCart(gql, vars),\n getCartItem: (biId) => cart.getCartItem(gql, biId),\n updateCartItem: (args: UpdateCartItemArgs) => cart.updateCartItem(gql, args),\n updateCartConsent: (args: UpdateCartConsentArgs) => cart.updateCartConsent(gql, args),\n waitForCartBusinessProcess: (biId) => cart.waitForCartBusinessProcess(gql, biId),\n placeOrder: (cartUid) => cart.placeOrder(gql, cartUid),\n getCartStatus: fetchStatus,\n findSubscriptionBiId: (biId) => cart.findSubscriptionBiId(gql, biId),\n getSalableUid: (maId) => bootstrap.getSalableUid(gql, maId),\n getBillingPlans: () => bootstrap.getBillingPlans(gql),\n getActiveSubscriptions: () => bootstrap.getActiveSubscriptions(gql),\n isCurrentUserSubscriber: () => bootstrap.isCurrentUserSubscriber(gql),\n getCourseDetail: (courseKey) => bootstrap.getCourseDetail(gql, courseKey),\n };\n}\n"]}
1
+ {"version":3,"file":"checkout.js","sourceRoot":"","sources":["../../src/utils/checkout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAC;AAS5C,MAAM,UAAU,oBAAoB,CAClC,EAAE,MAAM,EAAwB;IAEhC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAEpE,OAAO;QACL,GAAG;QACH,QAAQ;QACR,UAAU,EAAE,CAAC,IAAa,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;QACzD,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;QAChD,WAAW,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC;QAClD,cAAc,EAAE,CAAC,IAAwB,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC;QAC5E,iBAAiB,EAAE,CAAC,IAA2B,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC;QACrF,0BAA0B,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,GAAG,EAAE,IAAI,CAAC;QAChF,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;QACtD,aAAa,EAAE,WAAW;QAC1B,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,IAAI,CAAC;QACpE,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC;QAC3D,eAAe,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC;QACrD,sBAAsB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,sBAAsB,CAAC,GAAG,CAAC;QACnE,uBAAuB,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,uBAAuB,CAAC,GAAG,CAAC;QACrE,eAAe,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,SAAS,CAAC;KAC1E,CAAC;AACJ,CAAC","sourcesContent":["/**\n * api/index.ts — composes the pieces into a single checkout client and\n * re-exports the API utilities.\n */\nimport { createGqlClient } from './graphqlClient';\nimport * as cart from './cartApi';\nimport * as bootstrap from './bootstrapApi';\nimport type {\n CheckoutClient, CartIds, UpdateCartItemArgs, UpdateCartConsentArgs,\n} from '../types';\n\ninterface CheckoutClientConfig {\n apiUrl: string,\n}\n\nexport function createCheckoutClient(\n { apiUrl }: CheckoutClientConfig,\n): CheckoutClient {\n const { gql, setToken } = createGqlClient({ apiUrl });\n const fetchStatus = (biId: string) => cart.getCartStatus(gql, biId);\n\n return {\n gql,\n setToken,\n createCart: (vars: CartIds) => cart.createCart(gql, vars),\n cancelCart: (biId) => cart.cancelCart(gql, biId),\n getCartItem: (biId) => cart.getCartItem(gql, biId),\n updateCartItem: (args: UpdateCartItemArgs) => cart.updateCartItem(gql, args),\n updateCartConsent: (args: UpdateCartConsentArgs) => cart.updateCartConsent(gql, args),\n waitForCartBusinessProcess: (biId) => cart.waitForCartBusinessProcess(gql, biId),\n placeOrder: (cartUid) => cart.placeOrder(gql, cartUid),\n getCartStatus: fetchStatus,\n findSubscriptionBiId: (biId) => cart.findSubscriptionBiId(gql, biId),\n getSalableUid: (maId) => bootstrap.getSalableUid(gql, maId),\n getBillingPlans: () => bootstrap.getBillingPlans(gql),\n getActiveSubscriptions: () => bootstrap.getActiveSubscriptions(gql),\n isCurrentUserSubscriber: () => bootstrap.isCurrentUserSubscriber(gql),\n getCourseDetail: (courseKey) => bootstrap.getCourseDetail(gql, courseKey),\n };\n}\n"]}
@@ -8,6 +8,19 @@
8
8
  * surface the error and the user can retry.
9
9
  */
10
10
  import type { GqlRunner } from '../types';
11
+ export interface GqlErrorEntry {
12
+ message?: string;
13
+ /** Server detail behind the message, e.g. a validator's refusal code (SUBS-1127). */
14
+ extensions?: unknown;
15
+ }
16
+ /**
17
+ * A GraphQL error response. Keeps the full `errors` list: a refused validation
18
+ * comes back as a generic message, with its reason in `extensions`.
19
+ */
20
+ export declare class GqlError extends Error {
21
+ readonly errors: GqlErrorEntry[];
22
+ constructor(message: string, errors: GqlErrorEntry[]);
23
+ }
11
24
  export declare function createGqlClient({ apiUrl }: {
12
25
  apiUrl: string;
13
26
  }): {
@@ -7,6 +7,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ /**
11
+ * A GraphQL error response. Keeps the full `errors` list: a refused validation
12
+ * comes back as a generic message, with its reason in `extensions`.
13
+ */
14
+ export class GqlError extends Error {
15
+ constructor(message, errors) {
16
+ super(message);
17
+ this.name = 'GqlError';
18
+ this.errors = errors;
19
+ }
20
+ }
10
21
  export function createGqlClient({ apiUrl }) {
11
22
  if (!apiUrl)
12
23
  throw new Error('createGqlClient: apiUrl is required');
@@ -30,7 +41,7 @@ export function createGqlClient({ apiUrl }) {
30
41
  const json = yield res.json();
31
42
  if ((_a = json.errors) === null || _a === void 0 ? void 0 : _a.length) {
32
43
  const msg = ((_b = json.errors[0]) === null || _b === void 0 ? void 0 : _b.message) || 'GraphQL error';
33
- throw new Error(msg);
44
+ throw new GqlError(msg, json.errors);
34
45
  }
35
46
  return json.data;
36
47
  });
@@ -1 +1 @@
1
- {"version":3,"file":"graphqlClient.js","sourceRoot":"","sources":["../../src/utils/graphqlClient.ts"],"names":[],"mappings":";;;;;;;;;AAiBA,MAAM,UAAU,eAAe,CAC7B,EAAE,MAAM,EAAsB;IAE9B,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAEpE,IAAI,KAAK,GAAkB,IAAI,CAAC;IAEhC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAQ,EAAE;QACrC,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,GAAG,EAAE,CAAC;IAC5D,CAAC,CAAC;IAEF,MAAM,GAAG,GAAc,kCAAwC,EAAE,uEAAnC,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,KAAK;;QAC3D,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QAE7F,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;YAC9B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,EAAE;YACrE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;SAC3C,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACjC,KAAK,GAAG,IAAI,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,IAAI,GAAgB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,MAAA,IAAI,CAAC,MAAM,0CAAE,MAAM,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,CAAA,MAAA,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,0CAAE,OAAO,KAAI,eAAe,CAAC;YACvD,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC,CAAA,CAAC;IAEF,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * api/graphqlClient.ts — authed GraphQL client.\n *\n * No token is required at construction. Call setToken(viaxToken) after\n * GET /edx-session returns — typically on checkout page mount.\n *\n * On 401 the token is cleared and the request fails — the mutation will\n * surface the error and the user can retry.\n */\nimport type { GqlRunner } from '../types';\n\ninterface GqlResponse {\n\n data?: any,\n errors?: { message?: string }[],\n}\n\nexport function createGqlClient(\n { apiUrl }: { apiUrl: string },\n): { gql: GqlRunner, setToken: (token: string) => void } {\n if (!apiUrl) throw new Error('createGqlClient: apiUrl is required');\n\n let token: string | null = null;\n\n const setToken = (raw: string): void => {\n token = raw.startsWith('Bearer ') ? raw : `Bearer ${raw}`;\n };\n\n const gql: GqlRunner = async (query, variables, retry = false) => {\n if (!token) throw new Error('No viax token available. Please try placing your order again.');\n\n const res = await fetch(apiUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: token },\n body: JSON.stringify({ query, variables }),\n });\n\n if (res.status === 401 && !retry) {\n token = null;\n throw new Error('Session expired. Please try placing your order again.');\n }\n\n const json: GqlResponse = await res.json();\n if (json.errors?.length) {\n const msg = json.errors[0]?.message || 'GraphQL error';\n throw new Error(msg);\n }\n return json.data;\n };\n\n return { gql, setToken };\n}\n"]}
1
+ {"version":3,"file":"graphqlClient.js","sourceRoot":"","sources":["../../src/utils/graphqlClient.ts"],"names":[],"mappings":";;;;;;;;;AAuBA;;;GAGG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGjC,YAAY,OAAe,EAAE,MAAuB;QAClD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAC7B,EAAE,MAAM,EAAsB;IAE9B,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAEpE,IAAI,KAAK,GAAkB,IAAI,CAAC;IAEhC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAQ,EAAE;QACrC,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,GAAG,EAAE,CAAC;IAC5D,CAAC,CAAC;IAEF,MAAM,GAAG,GAAc,kCAAwC,EAAE,uEAAnC,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,KAAK;;QAC3D,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QAE7F,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;YAC9B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,EAAE;YACrE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;SAC3C,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACjC,KAAK,GAAG,IAAI,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,IAAI,GAAgB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,MAAA,IAAI,CAAC,MAAM,0CAAE,MAAM,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,CAAA,MAAA,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,0CAAE,OAAO,KAAI,eAAe,CAAC;YACvD,MAAM,IAAI,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC,CAAA,CAAC;IAEF,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * api/graphqlClient.ts — authed GraphQL client.\n *\n * No token is required at construction. Call setToken(viaxToken) after\n * GET /edx-session returns — typically on checkout page mount.\n *\n * On 401 the token is cleared and the request fails — the mutation will\n * surface the error and the user can retry.\n */\nimport type { GqlRunner } from '../types';\n\nexport interface GqlErrorEntry {\n message?: string,\n /** Server detail behind the message, e.g. a validator's refusal code (SUBS-1127). */\n extensions?: unknown,\n}\n\ninterface GqlResponse {\n\n data?: any,\n errors?: GqlErrorEntry[],\n}\n\n/**\n * A GraphQL error response. Keeps the full `errors` list: a refused validation\n * comes back as a generic message, with its reason in `extensions`.\n */\nexport class GqlError extends Error {\n readonly errors: GqlErrorEntry[];\n\n constructor(message: string, errors: GqlErrorEntry[]) {\n super(message);\n this.name = 'GqlError';\n this.errors = errors;\n }\n}\n\nexport function createGqlClient(\n { apiUrl }: { apiUrl: string },\n): { gql: GqlRunner, setToken: (token: string) => void } {\n if (!apiUrl) throw new Error('createGqlClient: apiUrl is required');\n\n let token: string | null = null;\n\n const setToken = (raw: string): void => {\n token = raw.startsWith('Bearer ') ? raw : `Bearer ${raw}`;\n };\n\n const gql: GqlRunner = async (query, variables, retry = false) => {\n if (!token) throw new Error('No viax token available. Please try placing your order again.');\n\n const res = await fetch(apiUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: token },\n body: JSON.stringify({ query, variables }),\n });\n\n if (res.status === 401 && !retry) {\n token = null;\n throw new Error('Session expired. Please try placing your order again.');\n }\n\n const json: GqlResponse = await res.json();\n if (json.errors?.length) {\n const msg = json.errors[0]?.message || 'GraphQL error';\n throw new GqlError(msg, json.errors);\n }\n return json.data;\n };\n\n return { gql, setToken };\n}\n"]}
@@ -12,6 +12,12 @@
12
12
  */
13
13
  /** Creates a new cart during checkout. */
14
14
  export declare const CREATE_CART = "mutation CreateCart($input: TwouCartInput!) {\n createTwouCart(input: $input) { biId uid }\n}";
15
+ /**
16
+ * Cancels the learner's own open cart: abandons it and cancels any Stripe /
17
+ * PayPal subscription still pending on it (SUBS-1127). Object scalar — no
18
+ * subfield selection; returns { ok, error? }.
19
+ */
20
+ export declare const CANCEL_CART = "mutation CartCancel($biId: String!) {\n tCartCancel(biId: $biId)\n}";
15
21
  /** Updates a cart item — attach billing plan / payment method. */
16
22
  export declare const UPDATE_CART_ITEM = "mutation UpdateCartItem($input: TwouCartItemInput!) {\n updateTwouCartItem(input: $input) { uid }\n}";
17
23
  /** Persists auto-renewal consent fields on the TwouCart BI (SUBS-228). */
@@ -15,6 +15,14 @@
15
15
  export const CREATE_CART = `mutation CreateCart($input: TwouCartInput!) {
16
16
  createTwouCart(input: $input) { biId uid }
17
17
  }`;
18
+ /**
19
+ * Cancels the learner's own open cart: abandons it and cancels any Stripe /
20
+ * PayPal subscription still pending on it (SUBS-1127). Object scalar — no
21
+ * subfield selection; returns { ok, error? }.
22
+ */
23
+ export const CANCEL_CART = `mutation CartCancel($biId: String!) {
24
+ tCartCancel(biId: $biId)
25
+ }`;
18
26
  /** Updates a cart item — attach billing plan / payment method. */
19
27
  export const UPDATE_CART_ITEM = `mutation UpdateCartItem($input: TwouCartItemInput!) {
20
28
  updateTwouCartItem(input: $input) { uid }
@@ -1 +1 @@
1
- {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/utils/queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iFAAiF;AAEjF,0CAA0C;AAC1C,MAAM,CAAC,MAAM,WAAW,GAAG;;EAEzB,CAAC;AAEH,kEAAkE;AAClE,MAAM,CAAC,MAAM,gBAAgB,GAAG;;EAE9B,CAAC;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,WAAW,GAAG;;EAEzB,CAAC;AAEH,wDAAwD;AACxD,MAAM,CAAC,MAAM,WAAW,GAAG;;EAEzB,CAAC;AAEH,iFAAiF;AAEjF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;EAK3B,CAAC;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,yBAAyB,GAAG;;;;EAIvC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;EAU7B,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;EAwB/B,CAAC;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;EAyB1B,CAAC;AAEH,iFAAiF;AACjF,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;EAWzB,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;EAe/B,CAAC;AAEH,kEAAkE;AAClE,MAAM,CAAC,MAAM,8BAA8B,GAAG;;EAE5C,CAAC;AAEH;0GAC0G;AAC1G,MAAM,CAAC,MAAM,8BAA8B,GAAG;;EAE5C,CAAC;AAEH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4B/B,CAAC","sourcesContent":["/**\n * api/queries.ts — GraphQL documents for the checkout flow.\n * Kept as plain strings so they can be reused, logged, or tested in isolation.\n *\n * Mutation reference (from API doc):\n * updateTwouCustomer — toggle Stripe test clock on/off\n * createTwouCart — user proceeds to payment\n * updateTwouCartItem — attach billing plan / payment method to cart\n * updateTwouCart — persist auto-renewal consent (SUBS-228)\n * startBusinessProcessableTransitionV2 — place order\n * updateTwouCartItem — retry: attach new payment method\n */\n\n// ─── Mutations ────────────────────────────────────────────────────────────────\n\n/** Creates a new cart during checkout. */\nexport const CREATE_CART = `mutation CreateCart($input: TwouCartInput!) {\n createTwouCart(input: $input) { biId uid }\n}`;\n\n/** Updates a cart item — attach billing plan / payment method. */\nexport const UPDATE_CART_ITEM = `mutation UpdateCartItem($input: TwouCartItemInput!) {\n updateTwouCartItem(input: $input) { uid }\n}`;\n\n/** Persists auto-renewal consent fields on the TwouCart BI (SUBS-228). */\nexport const UPDATE_CART = `mutation UpdateCart($input: TwouCartInput!) {\n updateTwouCart(input: $input) { uid }\n}`;\n\n/** Triggers the backend workflow to place the order. */\nexport const PLACE_ORDER = `mutation PlaceOrder($input: StartBusinessProcessableTransitionInputV2!) {\n startBusinessProcessableTransitionV2(input: $input) { uid bpStatus { code } }\n}`;\n\n// ─── Queries ──────────────────────────────────────────────────────────────────\n\nexport const GET_CART_ITEM = `query GetCartItem($biId: String!) {\n getBusinessInteraction(biId: $biId) {\n uid\n hiConsistsOf { ... on TwouCartItem { uid } }\n }\n}`;\n\n/** Checks whether the server has attached the cart's workflow yet. */\nexport const GET_CART_BUSINESS_PROCESS = `query GetCartBusinessProcess($biId: String!) {\n getBusinessInteraction(biId: $biId) {\n bpBusinessProcess { uid }\n }\n}`;\n\nexport const GET_CART_STATUS = `query GetCartStatus($biId: String!) {\n getBusinessInteraction(biId: $biId) {\n bpStatus { code }\n hiConsistsOf { ... on TwouCartItem { tStripeSubscriptionId } }\n ... on TwouCart {\n tStripeConfirmationRedirectUrl\n tPaypalApprovalUrl\n cmComments { edges { node { comText } } }\n }\n }\n}`;\n\nexport const FIND_SUBSCRIPTION = `query FindSubscription($filter: String!) {\n filterTwouSubscription(_first: 1, _filter: $filter) {\n edges {\n node {\n biId\n tRenewalType\n hiConsistsOf {\n ... on TwouSubscriptionItem {\n paybBillingPlan {\n tStripePriceId\n bipOwnershipModel { omName }\n }\n paybPayment {\n ... on TwouStripePayment {\n tStripePaymentMethodId\n tPaymentMethodLast4\n tPaymentMethodBrand\n }\n }\n }\n }\n }\n }\n }\n}`;\n\n/** Fetch the viax customer uid by email using filterTwouCustomer. */\nexport const GET_CUSTOMER = `query GetCustomer($filter: String!) {\n filterTwouCustomer(_first: 1, _filter: $filter) {\n edges {\n node {\n uid\n parId\n parParty {\n paName\n paContacts {\n edges {\n node {\n ... on EmailAddressContactMedium {\n email\n }\n }\n }\n }\n ... on Individual {\n indFirstName\n indLastName\n }\n }\n }\n }\n }\n}`;\n\n/** Fetch the TwouSubscriptionPlan uid to use as biiSalable in createTwouCart. */\nexport const GET_PRODUCT = `query GetProduct($maId: String!) {\n getProduct(maId: $maId) {\n maName\n uid\n ... on TwouSubscriptionPlan {\n saOwnershipModels {\n uid\n omName\n }\n }\n }\n}`;\n\nexport const GET_BILLING_PLANS = `query GetBillingPlans {\n filterCustomerBillingPlan(_first: 10) {\n edges {\n node {\n uid\n tStripePriceId\n tStripeAmount {\n amount\n units { code }\n }\n bipIsTemplate\n bipOwnershipModel { uid omName }\n }\n }\n }\n}`;\n\n/** Fetch the number of active subscriptions — unauthenticated. */\nexport const GET_ACTIVE_SUBSCRIPTIONS_STATE = `query activeSubscriptions {\n tActiveSubscriptionsState\n}`;\n\n/** Check whether the currently authenticated user already has an active subscription.\n * tIsCurrentUserSubscriber is a JSON scalar — returns the full object, no subfield selection allowed. */\nexport const GET_IS_CURRENT_USER_SUBSCRIBER = `query IsCurrentUserSubscriber {\n tIsCurrentUserSubscriber\n}`;\n\n/** Fetch a course for the enrollment card when ?course_run_key= is present. */\nexport const GET_COURSE_DETAIL = `query CourseDetail($filter: String!) {\n filterTwouCourse(_first: 1, _filter: $filter) {\n edges {\n node {\n maId\n maName\n maDescription\n tShortDescription\n imageUrl\n tCourseKey\n tBrandText\n tPrimarySubjectArea\n tLevelType\n tLanguage\n tPacingType { code }\n tDurationLow\n tDurationHigh\n tDurationUnit { code }\n tEffortLow\n tEffortHigh\n tEffortUnit { code }\n tUrlCourse\n tB2cSubscriptionInclusion\n tHasOfacRestrictions\n tCatalogActive\n }\n }\n }\n}`;\n"]}
1
+ {"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/utils/queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iFAAiF;AAEjF,0CAA0C;AAC1C,MAAM,CAAC,MAAM,WAAW,GAAG;;EAEzB,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;;EAEzB,CAAC;AAEH,kEAAkE;AAClE,MAAM,CAAC,MAAM,gBAAgB,GAAG;;EAE9B,CAAC;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,WAAW,GAAG;;EAEzB,CAAC;AAEH,wDAAwD;AACxD,MAAM,CAAC,MAAM,WAAW,GAAG;;EAEzB,CAAC;AAEH,iFAAiF;AAEjF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;EAK3B,CAAC;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,yBAAyB,GAAG;;;;EAIvC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;EAU7B,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;EAwB/B,CAAC;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;EAyB1B,CAAC;AAEH,iFAAiF;AACjF,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;EAWzB,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;EAe/B,CAAC;AAEH,kEAAkE;AAClE,MAAM,CAAC,MAAM,8BAA8B,GAAG;;EAE5C,CAAC;AAEH;0GAC0G;AAC1G,MAAM,CAAC,MAAM,8BAA8B,GAAG;;EAE5C,CAAC;AAEH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4B/B,CAAC","sourcesContent":["/**\n * api/queries.ts — GraphQL documents for the checkout flow.\n * Kept as plain strings so they can be reused, logged, or tested in isolation.\n *\n * Mutation reference (from API doc):\n * updateTwouCustomer — toggle Stripe test clock on/off\n * createTwouCart — user proceeds to payment\n * updateTwouCartItem — attach billing plan / payment method to cart\n * updateTwouCart — persist auto-renewal consent (SUBS-228)\n * startBusinessProcessableTransitionV2 — place order\n * updateTwouCartItem — retry: attach new payment method\n */\n\n// ─── Mutations ────────────────────────────────────────────────────────────────\n\n/** Creates a new cart during checkout. */\nexport const CREATE_CART = `mutation CreateCart($input: TwouCartInput!) {\n createTwouCart(input: $input) { biId uid }\n}`;\n\n/**\n * Cancels the learner's own open cart: abandons it and cancels any Stripe /\n * PayPal subscription still pending on it (SUBS-1127). Object scalar — no\n * subfield selection; returns { ok, error? }.\n */\nexport const CANCEL_CART = `mutation CartCancel($biId: String!) {\n tCartCancel(biId: $biId)\n}`;\n\n/** Updates a cart item — attach billing plan / payment method. */\nexport const UPDATE_CART_ITEM = `mutation UpdateCartItem($input: TwouCartItemInput!) {\n updateTwouCartItem(input: $input) { uid }\n}`;\n\n/** Persists auto-renewal consent fields on the TwouCart BI (SUBS-228). */\nexport const UPDATE_CART = `mutation UpdateCart($input: TwouCartInput!) {\n updateTwouCart(input: $input) { uid }\n}`;\n\n/** Triggers the backend workflow to place the order. */\nexport const PLACE_ORDER = `mutation PlaceOrder($input: StartBusinessProcessableTransitionInputV2!) {\n startBusinessProcessableTransitionV2(input: $input) { uid bpStatus { code } }\n}`;\n\n// ─── Queries ──────────────────────────────────────────────────────────────────\n\nexport const GET_CART_ITEM = `query GetCartItem($biId: String!) {\n getBusinessInteraction(biId: $biId) {\n uid\n hiConsistsOf { ... on TwouCartItem { uid } }\n }\n}`;\n\n/** Checks whether the server has attached the cart's workflow yet. */\nexport const GET_CART_BUSINESS_PROCESS = `query GetCartBusinessProcess($biId: String!) {\n getBusinessInteraction(biId: $biId) {\n bpBusinessProcess { uid }\n }\n}`;\n\nexport const GET_CART_STATUS = `query GetCartStatus($biId: String!) {\n getBusinessInteraction(biId: $biId) {\n bpStatus { code }\n hiConsistsOf { ... on TwouCartItem { tStripeSubscriptionId } }\n ... on TwouCart {\n tStripeConfirmationRedirectUrl\n tPaypalApprovalUrl\n cmComments { edges { node { comText } } }\n }\n }\n}`;\n\nexport const FIND_SUBSCRIPTION = `query FindSubscription($filter: String!) {\n filterTwouSubscription(_first: 1, _filter: $filter) {\n edges {\n node {\n biId\n tRenewalType\n hiConsistsOf {\n ... on TwouSubscriptionItem {\n paybBillingPlan {\n tStripePriceId\n bipOwnershipModel { omName }\n }\n paybPayment {\n ... on TwouStripePayment {\n tStripePaymentMethodId\n tPaymentMethodLast4\n tPaymentMethodBrand\n }\n }\n }\n }\n }\n }\n }\n}`;\n\n/** Fetch the viax customer uid by email using filterTwouCustomer. */\nexport const GET_CUSTOMER = `query GetCustomer($filter: String!) {\n filterTwouCustomer(_first: 1, _filter: $filter) {\n edges {\n node {\n uid\n parId\n parParty {\n paName\n paContacts {\n edges {\n node {\n ... on EmailAddressContactMedium {\n email\n }\n }\n }\n }\n ... on Individual {\n indFirstName\n indLastName\n }\n }\n }\n }\n }\n}`;\n\n/** Fetch the TwouSubscriptionPlan uid to use as biiSalable in createTwouCart. */\nexport const GET_PRODUCT = `query GetProduct($maId: String!) {\n getProduct(maId: $maId) {\n maName\n uid\n ... on TwouSubscriptionPlan {\n saOwnershipModels {\n uid\n omName\n }\n }\n }\n}`;\n\nexport const GET_BILLING_PLANS = `query GetBillingPlans {\n filterCustomerBillingPlan(_first: 10) {\n edges {\n node {\n uid\n tStripePriceId\n tStripeAmount {\n amount\n units { code }\n }\n bipIsTemplate\n bipOwnershipModel { uid omName }\n }\n }\n }\n}`;\n\n/** Fetch the number of active subscriptions — unauthenticated. */\nexport const GET_ACTIVE_SUBSCRIPTIONS_STATE = `query activeSubscriptions {\n tActiveSubscriptionsState\n}`;\n\n/** Check whether the currently authenticated user already has an active subscription.\n * tIsCurrentUserSubscriber is a JSON scalar — returns the full object, no subfield selection allowed. */\nexport const GET_IS_CURRENT_USER_SUBSCRIBER = `query IsCurrentUserSubscriber {\n tIsCurrentUserSubscriber\n}`;\n\n/** Fetch a course for the enrollment card when ?course_run_key= is present. */\nexport const GET_COURSE_DETAIL = `query CourseDetail($filter: String!) {\n filterTwouCourse(_first: 1, _filter: $filter) {\n edges {\n node {\n maId\n maName\n maDescription\n tShortDescription\n imageUrl\n tCourseKey\n tBrandText\n tPrimarySubjectArea\n tLevelType\n tLanguage\n tPacingType { code }\n tDurationLow\n tDurationHigh\n tDurationUnit { code }\n tEffortLow\n tEffortHigh\n tEffortUnit { code }\n tUrlCourse\n tB2cSubscriptionInclusion\n tHasOfacRestrictions\n tCatalogActive\n }\n }\n }\n}`;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edx/frontend-app-subscription-checkout",
3
- "version": "1.22.0",
3
+ "version": "1.22.2",
4
4
  "description": "Subscription Checkout Frontend application",
5
5
  "repository": {
6
6
  "type": "git",