@edx/frontend-app-alacarte-checkout 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.rst +144 -0
  2. package/dist/checkoutPage/CheckoutPage.js +1 -2
  3. package/dist/checkoutPage/CheckoutPage.js.map +1 -1
  4. package/dist/components/Checkout/Checkout.js +5 -1
  5. package/dist/components/Checkout/Checkout.js.map +1 -1
  6. package/dist/components/CheckoutGate/CheckoutGate.d.ts +1 -3
  7. package/dist/components/CheckoutGate/CheckoutGate.js +1 -3
  8. package/dist/components/CheckoutGate/CheckoutGate.js.map +1 -1
  9. package/dist/components/ExpressCheckout/ExpressCheckout.d.ts +9 -1
  10. package/dist/components/ExpressCheckout/ExpressCheckout.js +177 -49
  11. package/dist/components/ExpressCheckout/ExpressCheckout.js.map +1 -1
  12. package/dist/components/ExpressCheckout/Messages.d.ts +5 -0
  13. package/dist/components/ExpressCheckout/Messages.js +5 -0
  14. package/dist/components/ExpressCheckout/Messages.js.map +1 -1
  15. package/dist/components/PaymentForm/PaymentForm.js +4 -1
  16. package/dist/components/PaymentForm/PaymentForm.js.map +1 -1
  17. package/dist/constants.d.ts +14 -1
  18. package/dist/constants.js +10 -5
  19. package/dist/constants.js.map +1 -1
  20. package/dist/data/hooks/useCheckout.js +21 -3
  21. package/dist/data/hooks/useCheckout.js.map +1 -1
  22. package/dist/data/hooks/useCheckoutGate.d.ts +5 -10
  23. package/dist/data/hooks/useCheckoutGate.js +25 -66
  24. package/dist/data/hooks/useCheckoutGate.js.map +1 -1
  25. package/dist/data/hooks/useCheckoutQueries.d.ts +3 -22
  26. package/dist/data/hooks/useCheckoutQueries.js +6 -52
  27. package/dist/data/hooks/useCheckoutQueries.js.map +1 -1
  28. package/dist/routes.js +21 -2
  29. package/dist/routes.js.map +1 -1
  30. package/dist/styles/components/expressCheckout.scss +43 -17
  31. package/dist/types.d.ts +56 -9
  32. package/dist/types.js.map +1 -1
  33. package/dist/utils/DevBypassAuthService.d.ts +42 -0
  34. package/dist/utils/DevBypassAuthService.js +82 -0
  35. package/dist/utils/DevBypassAuthService.js.map +1 -0
  36. package/dist/utils/cartApi.js +12 -9
  37. package/dist/utils/cartApi.js.map +1 -1
  38. package/dist/utils/provision.d.ts +3 -36
  39. package/dist/utils/provision.js +8 -66
  40. package/dist/utils/provision.js.map +1 -1
  41. package/dist/utils/queries.d.ts +1 -1
  42. package/dist/utils/queries.js +2 -2
  43. package/dist/utils/queries.js.map +1 -1
  44. package/package.json +4 -1
  45. package/dist/utils/url.d.ts +0 -6
  46. package/dist/utils/url.js +0 -20
  47. package/dist/utils/url.js.map +0 -1
package/README.rst CHANGED
@@ -92,6 +92,150 @@ Bind mounts are used instead of symlinks because Node resolves symlinks to
92
92
  their real paths, which breaks hoisted dependency resolution. When you are
93
93
  done, unmount with ``sudo umount packages/frontend-base``.
94
94
 
95
+ Testing Apple Pay and Google Pay
96
+ ================================
97
+
98
+ Wallets require a **trusted HTTPS** origin. Plain ``http://localhost`` will
99
+ not show Apple Pay / Google Pay. For local work use mkcert HTTPS, then an
100
+ ngrok tunnel when you need Apple Pay domain verification.
101
+
102
+ What you need
103
+ -------------
104
+
105
+ - macOS with `mkcert`_ (``brew install mkcert nss``)
106
+ - `ngrok`_ (recent agent; run ``ngrok update`` if auth fails with ERR_NGROK_121)
107
+ - Stripe **test** publishable key in ``site.config.dev.https.tsx``
108
+ - A card in Apple Wallet (Safari / Apple Pay) and/or Google Pay (Chrome)
109
+ - Optional: a product URL such as
110
+ ``/aca-checkout?program_uid=<uid>``
111
+
112
+ .. _mkcert: https://github.com/FiloSottile/mkcert
113
+ .. _ngrok: https://ngrok.com/download
114
+
115
+ 1. Generate and trust local certificates
116
+ ----------------------------------------
117
+
118
+ .. code-block:: sh
119
+
120
+ # One-time: install the mkcert CA into the system trust store
121
+ mkcert -install
122
+
123
+ # Per repo: write certs into .certs/ (gitignored)
124
+ npm run certs:generate
125
+
126
+ This creates:
127
+
128
+ - ``.certs/localhost+2.pem``
129
+ - ``.certs/localhost+2-key.pem``
130
+
131
+ covering ``localhost``, ``127.0.0.1``, and ``::1``. The browser must show a
132
+ normal lock on ``https://localhost:1996`` (not “Not Secure”). If it still
133
+ warns, re-run ``mkcert -install``, regenerate certs, fully quit the browser,
134
+ and reopen.
135
+
136
+ Do **not** open the LAN IP from the webpack banner
137
+ (``https://192.168.x.x:1996``) — that host is not on the certificate.
138
+
139
+ 2. Start the HTTPS dev server
140
+ -----------------------------
141
+
142
+ .. code-block:: sh
143
+
144
+ npm run dev:https
145
+
146
+ This uses ``site.config.dev.https.tsx``, which:
147
+
148
+ - Serves over HTTPS with the mkcert files above
149
+ - Sets ``DEV_MOCK_SESSION`` so Viax tokens work without the LMS session
150
+ endpoint (CORS breaks on ``https://localhost`` / ngrok)
151
+ - Uses ``DevBypassAuthService`` so the shell does not call
152
+ ``http://localhost:18000/login_refresh`` (which would hang or redirect
153
+ when Django is down or the origin is ngrok)
154
+
155
+ App URL (example)::
156
+
157
+ https://localhost:1996/aca-checkout?program_uid=<uid>
158
+
159
+ Google Pay can often be exercised on this URL in **Chrome**. Apple Pay on
160
+ ``localhost`` is usually blocked until the domain is registered with Stripe;
161
+ use the tunnel steps below for Apple Pay.
162
+
163
+ 3. Tunnel with ngrok (required for Apple Pay)
164
+ ---------------------------------------------
165
+
166
+ ``localhost`` cannot be registered as an Apple Pay domain in Stripe. Expose
167
+ the HTTPS app with ngrok (keep ``npm run dev:https`` running):
168
+
169
+ .. code-block:: sh
170
+
171
+ npm run dev:tunnel
172
+ # equivalent: ngrok http https://localhost:1996
173
+
174
+ Copy the **https** forwarding host only, e.g.
175
+ ``abc123.ngrok-free.app`` (no ``https://``, no path, no port).
176
+
177
+ If you see ``ERR_NGROK_3004``, the tunnel is hitting HTTP while the app
178
+ speaks HTTPS — use ``ngrok http https://localhost:1996`` (already wired in
179
+ ``dev:tunnel``).
180
+
181
+ Free ngrok URLs change every restart; you must re-register each new host in
182
+ Stripe (or use a reserved ngrok domain).
183
+
184
+ 4. Register the domain in Stripe
185
+ --------------------------------
186
+
187
+ 1. Open Stripe Dashboard → **Test mode** →
188
+ `Payment method domains`_
189
+ 2. **Add a new domain** → paste the ngrok host
190
+ (``abc123.ngrok-free.app``)
191
+ 3. Save
192
+
193
+ .. _Payment method domains: https://dashboard.stripe.com/test/settings/payment_method_domains
194
+
195
+ Then open checkout on the **ngrok** origin, not localhost::
196
+
197
+ https://abc123.ngrok-free.app/aca-checkout?program_uid=<uid>
198
+
199
+ 5. Which browser for which wallet
200
+ ---------------------------------
201
+
202
+ +------------------+----------------------------------------------------------+
203
+ | Wallet | Where to test |
204
+ +==================+==========================================================+
205
+ | Google Pay | Chrome (signed into a Google account). Can work on |
206
+ | | ``https://localhost`` once the cert is trusted. |
207
+ +------------------+----------------------------------------------------------+
208
+ | Apple Pay | Safari (Mac/iPhone) with a card in Wallet. Use the |
209
+ | | **registered ngrok** HTTPS URL. Chrome on macOS can |
210
+ | | also show Apple Pay when the domain is registered and |
211
+ | | ``applePay: 'always'`` is set (this app does). |
212
+ +------------------+----------------------------------------------------------+
213
+ | PayPal button | Always rendered (placeholder / disabled in express UI). |
214
+ +------------------+----------------------------------------------------------+
215
+
216
+ Express checkout mounts Apple Pay and Google Pay as separate Stripe Express
217
+ Checkout Elements in one row (PayPal | Apple Pay | Google Pay). Console
218
+ logs::
219
+
220
+ [ExpressCheckout] ready (apple) { applePay: true/false, … }
221
+ [ExpressCheckout] ready (google) { googlePay: true/false, … }
222
+
223
+ 6. Checklist when buttons are missing
224
+ -------------------------------------
225
+
226
+ 1. Padlock is clean (trusted HTTPS), URL is ``localhost`` or your ngrok host
227
+ — not a LAN IP.
228
+ 2. ``npm run dev:https`` is running (not plain ``npm run dev``).
229
+ 3. For Apple Pay: ngrok host is registered in Stripe **test** Payment method
230
+ domains and matches the URL bar exactly.
231
+ 4. Browser matches the wallet (Safari ≈ Apple Pay, Chrome ≈ Google Pay).
232
+ 5. ``DEV_MOCK_SESSION_RESPONSE`` tokens in ``site.config.dev.https.tsx`` are
233
+ not expired (refresh from a real session when Viax calls start failing).
234
+ 6. You are not redirected to ``http://localhost:18000/dashboard`` — that means
235
+ LMS cookie auth ran; ``dev:https`` should use ``DevBypassAuthService``.
236
+ 7. Hard-refresh after cert or config changes; fully quit Chrome if it cached
237
+ an old certificate warning.
238
+
95
239
  Making Your New Project's README File
96
240
  =====================================
97
241
 
@@ -19,7 +19,6 @@ const CheckoutPage = () => {
19
19
  const isDevelopment = siteConfig.environment === 'development';
20
20
  const edxSessionUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/edx-session`;
21
21
  const geolocationUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/geolocation/lookup`;
22
- const complianceCheckUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/compliance-check`;
23
22
  // When the demo banner is enabled, start on the "PDP" view; clicking the
24
23
  // CTA flips showCheckout to true and the real checkout renders.
25
24
  const [showCheckout, setShowCheckout] = useState(!SHOW_DEMO_PDP);
@@ -29,7 +28,7 @@ const CheckoutPage = () => {
29
28
  if (!showCheckout) {
30
29
  return (_jsx(DemoPdpBanner, { apiUrl: apiUrl, edxSessionUrl: edxSessionUrl, onEnroll: () => setShowCheckout(true) }));
31
30
  }
32
- return (_jsx(PageWrapper, { children: _jsx(CheckoutGate, { stripePublishableKey: stripePublishableKey, apiUrl: apiUrl, edxSessionUrl: edxSessionUrl, geolocationUrl: geolocationUrl, complianceCheckUrl: complianceCheckUrl, learnerDashboardUrl: `${LMS_BASE_URL}/dashboard`, homeUrl: LMS_BASE_URL || '/', onComplete: (_subscriptionBiId) => {
31
+ return (_jsx(PageWrapper, { children: _jsx(CheckoutGate, { stripePublishableKey: stripePublishableKey, apiUrl: apiUrl, edxSessionUrl: edxSessionUrl, geolocationUrl: geolocationUrl, learnerDashboardUrl: `${LMS_BASE_URL}/dashboard`, homeUrl: LMS_BASE_URL || '/', onComplete: (_subscriptionBiId) => {
33
32
  console.log(_subscriptionBiId);
34
33
  // window.location.href = `/checkout/confirmation?sub=${_subscriptionBiId ?? ''}`;
35
34
  // Success screen.
@@ -1 +1 @@
1
- {"version":3,"file":"CheckoutPage.js","sourceRoot":"","sources":["../../src/checkoutPage/CheckoutPage.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,YAAY,MAAM,yCAAyC,CAAC;AACnE,OAAO,aAAa,MAAM,2CAA2C,CAAC;AAEtE,MAAM,WAAW,GAA4C,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAC7E,KAAC,SAAS,cAAE,QAAQ,GAAa,CAClC,CAAC;AAEF,uEAAuE;AACvE,0EAA0E;AAC1E,0EAA0E;AAC1E,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,YAAY,GAAa,GAAG,EAAE;IAClC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IACnC,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9D,MAAM,oBAAoB,GAAG,MAAM,CAAC,iBAA2B,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,CAAC,YAAsB,CAAC;IAC7C,MAAM,aAAa,GAAG,UAAU,CAAC,WAAW,KAAK,aAAa,CAAC;IAC/D,MAAM,aAAa,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,YAAY,kBAAkB,CAAC;IAC1G,MAAM,cAAc,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,YAAY,yBAAyB,CAAC;IAClH,MAAM,kBAAkB,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,YAAY,uBAAuB,CAAC;IAEpH,yEAAyE;IACzE,gEAAgE;IAChE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,CAAC,aAAa,CAAC,CAAC;IAEjE,IAAI,CAAC,oBAAoB,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QACvD,OAAO,CACL,KAAC,WAAW,cACV,MAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,2IAEwC,GAAG,EAChE,YAAG,IAAI,EAAC,4CAA4C,gCAEhD,SAEE,GACI,CACf,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,CACL,KAAC,aAAa,IACZ,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,GACrC,CACH,CAAC;IACJ,CAAC;IAED,OAAO,CACL,KAAC,WAAW,cACV,KAAC,YAAY,IACX,oBAAoB,EAAE,oBAAoB,EAC1C,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,cAAc,EAAE,cAAc,EAC9B,kBAAkB,EAAE,kBAAkB,EACtC,mBAAmB,EAAE,GAAG,YAAY,YAAY,EAChD,OAAO,EAAE,YAAY,IAAI,GAAG,EAC5B,UAAU,EAAE,CAAC,iBAAiB,EAAE,EAAE;gBAChC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;gBAC/B,kFAAkF;gBAClF,kBAAkB;YACpB,CAAC,GACD,GACU,CACf,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,YAAY,CAAC","sourcesContent":["import React, { useState } from 'react';\nimport { Alert, Container } from '@openedx/paragon';\nimport { getAppConfig, getSiteConfig } from '@openedx/frontend-base';\nimport { appId } from '@src/constants';\nimport CheckoutGate from '../components/CheckoutGate/CheckoutGate';\nimport DemoPdpBanner from '../components/DemoPdpBanner/DemoPdpBanner';\n\nconst PageWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (\n <Container>{children}</Container>\n);\n\n// TEMPORARY — Set to true to show the demo PDP banner before checkout.\n// When true, the banner is displayed first; clicking \"Earn a certificate\"\n// transitions to the real checkout flow. Set to false to skip the banner.\nconst SHOW_DEMO_PDP = true;\n\nconst CheckoutPage: React.FC = () => {\n const config = getAppConfig(appId);\n const siteConfig = getSiteConfig();\n const LMS_BASE_URL = siteConfig.lmsBaseUrl.replace(/\\/$/, '');\n const stripePublishableKey = config.STRIPE_PUBLIC_KEY as string;\n const apiUrl = config.VIAX_API_URL as string;\n const isDevelopment = siteConfig.environment === 'development';\n const edxSessionUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/edx-session`;\n const geolocationUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/geolocation/lookup`;\n const complianceCheckUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/compliance-check`;\n\n // When the demo banner is enabled, start on the \"PDP\" view; clicking the\n // CTA flips showCheckout to true and the real checkout renders.\n const [showCheckout, setShowCheckout] = useState(!SHOW_DEMO_PDP);\n\n if (!stripePublishableKey || !apiUrl || !edxSessionUrl) {\n return (\n <PageWrapper>\n <Alert variant=\"danger\">\n We&apos;re having trouble loading the checkout. Please refresh the\n page or try again in a few moments. If the problem persists,{' '}\n <a href=\"https://courses.edx.org/support/contact_us\">\n contact support\n </a>\n .\n </Alert>\n </PageWrapper>\n );\n }\n\n if (!showCheckout) {\n return (\n <DemoPdpBanner\n apiUrl={apiUrl}\n edxSessionUrl={edxSessionUrl}\n onEnroll={() => setShowCheckout(true)}\n />\n );\n }\n\n return (\n <PageWrapper>\n <CheckoutGate\n stripePublishableKey={stripePublishableKey}\n apiUrl={apiUrl}\n edxSessionUrl={edxSessionUrl}\n geolocationUrl={geolocationUrl}\n complianceCheckUrl={complianceCheckUrl}\n learnerDashboardUrl={`${LMS_BASE_URL}/dashboard`}\n homeUrl={LMS_BASE_URL || '/'}\n onComplete={(_subscriptionBiId) => {\n console.log(_subscriptionBiId);\n // window.location.href = `/checkout/confirmation?sub=${_subscriptionBiId ?? ''}`;\n // Success screen.\n }}\n />\n </PageWrapper>\n );\n};\n\nexport default CheckoutPage;\n"]}
1
+ {"version":3,"file":"CheckoutPage.js","sourceRoot":"","sources":["../../src/checkoutPage/CheckoutPage.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,YAAY,MAAM,yCAAyC,CAAC;AACnE,OAAO,aAAa,MAAM,2CAA2C,CAAC;AAEtE,MAAM,WAAW,GAA4C,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAC7E,KAAC,SAAS,cAAE,QAAQ,GAAa,CAClC,CAAC;AAEF,uEAAuE;AACvE,0EAA0E;AAC1E,0EAA0E;AAC1E,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,YAAY,GAAa,GAAG,EAAE;IAClC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IACnC,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9D,MAAM,oBAAoB,GAAG,MAAM,CAAC,iBAA2B,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,CAAC,YAAsB,CAAC;IAC7C,MAAM,aAAa,GAAG,UAAU,CAAC,WAAW,KAAK,aAAa,CAAC;IAC/D,MAAM,aAAa,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,YAAY,kBAAkB,CAAC;IAC1G,MAAM,cAAc,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,YAAY,yBAAyB,CAAC;IAElH,yEAAyE;IACzE,gEAAgE;IAChE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,CAAC,aAAa,CAAC,CAAC;IAEjE,IAAI,CAAC,oBAAoB,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QACvD,OAAO,CACL,KAAC,WAAW,cACV,MAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,2IAEwC,GAAG,EAChE,YAAG,IAAI,EAAC,4CAA4C,gCAEhD,SAEE,GACI,CACf,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,CACL,KAAC,aAAa,IACZ,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,GACrC,CACH,CAAC;IACJ,CAAC;IAED,OAAO,CACL,KAAC,WAAW,cACV,KAAC,YAAY,IACX,oBAAoB,EAAE,oBAAoB,EAC1C,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,cAAc,EAAE,cAAc,EAC9B,mBAAmB,EAAE,GAAG,YAAY,YAAY,EAChD,OAAO,EAAE,YAAY,IAAI,GAAG,EAC5B,UAAU,EAAE,CAAC,iBAAiB,EAAE,EAAE;gBAChC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;gBAC/B,kFAAkF;gBAClF,kBAAkB;YACpB,CAAC,GACD,GACU,CACf,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,YAAY,CAAC","sourcesContent":["import React, { useState } from 'react';\nimport { Alert, Container } from '@openedx/paragon';\nimport { getAppConfig, getSiteConfig } from '@openedx/frontend-base';\nimport { appId } from '@src/constants';\nimport CheckoutGate from '../components/CheckoutGate/CheckoutGate';\nimport DemoPdpBanner from '../components/DemoPdpBanner/DemoPdpBanner';\n\nconst PageWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (\n <Container>{children}</Container>\n);\n\n// TEMPORARY — Set to true to show the demo PDP banner before checkout.\n// When true, the banner is displayed first; clicking \"Earn a certificate\"\n// transitions to the real checkout flow. Set to false to skip the banner.\nconst SHOW_DEMO_PDP = true;\n\nconst CheckoutPage: React.FC = () => {\n const config = getAppConfig(appId);\n const siteConfig = getSiteConfig();\n const LMS_BASE_URL = siteConfig.lmsBaseUrl.replace(/\\/$/, '');\n const stripePublishableKey = config.STRIPE_PUBLIC_KEY as string;\n const apiUrl = config.VIAX_API_URL as string;\n const isDevelopment = siteConfig.environment === 'development';\n const edxSessionUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/edx-session`;\n const geolocationUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/geolocation/lookup`;\n\n // When the demo banner is enabled, start on the \"PDP\" view; clicking the\n // CTA flips showCheckout to true and the real checkout renders.\n const [showCheckout, setShowCheckout] = useState(!SHOW_DEMO_PDP);\n\n if (!stripePublishableKey || !apiUrl || !edxSessionUrl) {\n return (\n <PageWrapper>\n <Alert variant=\"danger\">\n We&apos;re having trouble loading the checkout. Please refresh the\n page or try again in a few moments. If the problem persists,{' '}\n <a href=\"https://courses.edx.org/support/contact_us\">\n contact support\n </a>\n .\n </Alert>\n </PageWrapper>\n );\n }\n\n if (!showCheckout) {\n return (\n <DemoPdpBanner\n apiUrl={apiUrl}\n edxSessionUrl={edxSessionUrl}\n onEnroll={() => setShowCheckout(true)}\n />\n );\n }\n\n return (\n <PageWrapper>\n <CheckoutGate\n stripePublishableKey={stripePublishableKey}\n apiUrl={apiUrl}\n edxSessionUrl={edxSessionUrl}\n geolocationUrl={geolocationUrl}\n learnerDashboardUrl={`${LMS_BASE_URL}/dashboard`}\n homeUrl={LMS_BASE_URL || '/'}\n onComplete={(_subscriptionBiId) => {\n console.log(_subscriptionBiId);\n // window.location.href = `/checkout/confirmation?sub=${_subscriptionBiId ?? ''}`;\n // Success screen.\n }}\n />\n </PageWrapper>\n );\n};\n\nexport default CheckoutPage;\n"]}
@@ -99,8 +99,12 @@ const Checkout = (_a) => {
99
99
  if (!c.stripe)
100
100
  throw new Error('Stripe not loaded');
101
101
  const stripeInstance = c.stripe;
102
+ const country = (c.billing.country || walletCountry || c.userCountry || '').trim().toUpperCase();
103
+ if (!country) {
104
+ throw new Error('Please select a country before using Apple Pay or Google Pay.');
105
+ }
102
106
  yield alacarte.placeOrderAsync({
103
- billing: Object.assign(Object.assign({}, c.billing), { country: c.billing.country || walletCountry || c.userCountry || '' }),
107
+ billing: Object.assign(Object.assign({}, c.billing), { country }),
104
108
  confirmCardPayment: (clientSecret) => __awaiter(void 0, void 0, void 0, function* () {
105
109
  yield confirmPaymentWithMethodId({
106
110
  stripe: stripeInstance,
@@ -1 +1 @@
1
- {"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA,OAAc,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAChE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,WAAW,MAAM,8BAA8B,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACjF,OAAO,WAAW,MAAM,4BAA4B,CAAC;AACrD,OAAO,iBAAiB,MAAM,wCAAwC,CAAC;AACvE,OAAO,WAAW,MAAM,4BAA4B,CAAC;AACrD,OAAO,uBAAuB,MAAM,oDAAoD,CAAC;AACzF,OAAO,cAAc,MAAM,kCAAkC,CAAC;AAC9D,OAAO,oBAAoB,MAAM,8CAA8C,CAAC;AAChF,OAAO,kBAAkB,MAAM,0CAA0C,CAAC,CAAC,kDAAkD;AAC7H,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,+BAA+B,EAAE,MAAM,4BAA4B,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,0BAA0B,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGhG,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC,8EAA8E;AAC9E,0EAA0E;AAC1E,6CAA6C;AAC7C,MAAM,8BAA8B,GAAG,KAAK,CAAC;AAW7C,MAAM,QAAQ,GAAG,CAAC,EAIF,EAAE,EAAE;;QAJF,EAChB,OAAO,GAAG,GAAG,EACb,eAAe,OAED,EADX,KAAK,cAHQ,8BAIjB,CADS;IAER,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC;IACnG,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAC7B,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACzD,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACtF,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxE,2EAA2E;IAC3E,qEAAqE;IACrE,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAA2C,IAAI,CAAC,CAAC;IACvG,MAAM,eAAe,GAAG,CAAC,QAAQ,CAAC,KAAK,KAAK,OAAO,IAAI,QAAQ,CAAC,KAAK,KAAK,cAAc,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;IACjH,MAAM,iBAAiB,GAAG,CAAA,MAAA,MAAA,QAAQ,CAAC,QAAQ,0CAAE,UAAU,0CAAE,IAAI,EAAE;QAC7D,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE;QACrC,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,eAAe,GAAG,sBAAsB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9D,2EAA2E;IAC3E,yEAAyE;IACzE,qEAAqE;IACrE,MAAM,gBAAgB,GAAG,CAAC,CAAC,CAAA,MAAA,QAAQ,CAAC,IAAI,0CAAE,QAAQ,CAAA;WAC7C,QAAQ,CAAC,IAAI,CAAC,cAAc,KAAK,WAAW;WAC5C,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,KAAK,QAAQ,IAAI,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACxE,MAAM,eAAe,GAAG,gBAAgB,IAAI,eAAe,IAAI,IAAI;QACjE,CAAC,CAAC;YACE,OAAO,EAAE,eAAe;YACxB,MAAM,EAAE,QAAQ,CAAC,IAAK,CAAC,QAAS,CAAC,MAAM;YACvC,QAAQ,EAAE,QAAQ,CAAC,IAAK,CAAC,QAAS,CAAC,QAAQ;SAC5C;QACH,CAAC,CAAC,IAAI,CAAC;IAET,uEAAuE;IACvE,6EAA6E;IAC7E,SAAS,CAAC,GAAG,EAAE;;QACb,IAAI,MAAA,QAAQ,CAAC,QAAQ,0CAAE,UAAU,EAAE,CAAC;YAClC,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QAClD,CAAC;IACH,CAAC,EAAE,CAAC,MAAA,QAAQ,CAAC,QAAQ,0CAAE,UAAU,CAAC,CAAC,CAAC;IAEpC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,iBAAiB,IAAI,gBAAgB,IAAI,eAAe,IAAI,IAAI,EAAE,CAAC;YACrE,kBAAkB,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC,EAAE,CAAC,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,CAAC,CAAC,CAAC;IAE3D,sEAAsE;IACtE,kDAAkD;IAClD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,8BAA8B,IAAI,QAAQ,CAAC,KAAK,KAAK,kBAAkB,EAAE,CAAC;YAC5E,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC;QACjC,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IAE9B,4EAA4E;IAC5E,gFAAgF;IAChF,MAAM,oBAAoB,GAAG,WAAW,CAAC,CAAO,eAAuB,EAAE,aAAsB,EAAE,EAAE;QACjG,IAAI,CAAC,CAAC,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACpD,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;QAEhC,MAAM,QAAQ,CAAC,eAAe,CAAC;YAC7B,OAAO,kCACF,CAAC,CAAC,OAAO,KACZ,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,aAAa,IAAI,CAAC,CAAC,WAAW,IAAI,EAAE,GACnE;YACD,kBAAkB,EAAE,CAAO,YAAoB,EAAE,EAAE;gBACjD,MAAM,0BAA0B,CAAC;oBAC/B,MAAM,EAAE,cAAc;oBACtB,YAAY;oBACZ,eAAe;iBAChB,CAAC,CAAC;YACL,CAAC,CAAA;SACF,CAAC,CAAC;IACL,CAAC,CAAA,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAEnD,MAAM,cAAc,GAChB,CAAC,CAAC,mBAAmB;WAClB,CAAC,CAAC,sBAAsB;WACxB,CAAC,CAAC,KAAK,KAAK,SAAS;WACrB,CAAC,CAAC,KAAK,KAAK,SAAS;WACrB,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC;IAC5B,MAAM,QAAQ,GACV,CAAC,cAAc;WACZ,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM;eACjB,CAAC,CAAC,KAAK,KAAK,OAAO;eACnB,CAAC,CAAC,KAAK,KAAK,QAAQ;eACpB,CAAC,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC;IACnC,MAAM,aAAa,GAAG,+BAA+B,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAErE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAEzD,MAAM,uBAAuB,GAAG,kBAAkB;WAC7C,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO;WACnB,CAAC,0BAA0B,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAE7D,MAAM,aAAa,GAAiB,CAAC,CAAC,aAAa,CAAC;IAEpD,6EAA6E;IAC7E,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,eAAe,GAAG,CAAA,MAAA,QAAQ,CAAC,IAAI,0CAAE,IAAI,MAAK,QAAQ,IAAI,CAAA,MAAA,QAAQ,CAAC,QAAQ,0CAAE,IAAI,MAAK,QAAQ,CAAC;IACjG,MAAM,gBAAgB,GAAG,CAAA,MAAA,QAAQ,CAAC,IAAI,0CAAE,IAAI,MAAK,SAAS,IAAI,CAAA,MAAA,QAAQ,CAAC,QAAQ,0CAAE,IAAI,MAAK,SAAS,CAAC;IAEpG,MAAM,oBAAoB,GACtB,eAAe,IAAI,QAAQ,CAAC,aAAa,IAAI,MAAM,IAAI,QAAQ,CAAC,aAAa;QAC7E,CAAC,CAAC,QAAQ,CAAC,aAAa;QACxB,CAAC,CAAC,IAAI,CAAC;IACX,MAAM,gBAAgB,GAAG,oBAAoB,aAApB,oBAAoB,cAApB,oBAAoB,GAAI,CAAC,CAAC,MAAM,CAAC;IAE1D,MAAM,qBAAqB,GACvB,gBAAgB,IAAI,QAAQ,CAAC,aAAa,IAAI,UAAU,IAAI,QAAQ,CAAC,aAAa;QAClF,CAAC,CAAC,QAAQ,CAAC,aAAa;QACxB,CAAC,CAAC,IAAI,CAAC;IAEX,MAAM,iBAAiB,GAAG,GAAG,EAAE;;QAC7B,MAAM,iBAAiB,GAAG,CAAA,oBAAoB,aAApB,oBAAoB,uBAApB,oBAAoB,CAAE,UAAU;eACrD,CAAC,QAAQ,CAAC,aAAa,IAAI,YAAY,IAAI,QAAQ,CAAC,aAAa;gBAClE,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,UAAU;gBACnC,CAAC,CAAC,IAAI,CAAC,CAAC;QACZ,IAAI,iBAAiB,EAAE,CAAC;YACtB,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC3C,CAAC;aAAM,IAAI,MAAA,CAAC,CAAC,MAAM,0CAAE,UAAU,EAAE,CAAC;YAChC,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,OAAO,IAAI,GAAG,CAAC;QACxC,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM;QAC1B,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACnE,CAAC,CAAC,IAAI,CAAC;IAET,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,KAAC,gBAAgB,KAAG,CAAC;IAC9B,CAAC;IAED,0EAA0E;IAC1E,8EAA8E;IAC9E,MAAM,mBAAmB,GAAG,KAAK,CAAC;IAClC,MAAM,aAAa,GAAG,mBAAmB;QACvC,CAAC,CAAC,KAAC,kBAAkB,IAAC,IAAI,EAAE,QAAQ,GAAI;QACxC,CAAC,CAAC,IAAI,CAAC;IAET,wEAAwE;IACxE,4EAA4E;IAC5E,iDAAiD;IACjD,IAAI,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QACtD,MAAM,iBAAiB,GAAG,CAAA,MAAA,QAAQ,CAAC,IAAI,0CAAE,IAAI,MAAK,SAAS;YACzD,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW;YAC3B,CAAC,CAAC,CAAA,MAAA,QAAQ,CAAC,IAAI,0CAAE,IAAI,MAAK,QAAQ;gBAChC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU;gBAC1B,CAAC,CAAC,IAAI,CAAC;QAEX,OAAO,CACL,8BACG,aAAa,EACd,KAAC,GAAG,IAAC,SAAS,EAAC,mEAAmE,YAChF,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,YACT,KAAC,oBAAoB,IACnB,KAAK,EAAE,QAAQ,CAAC,WAAW,EAC3B,QAAQ,EAAE,KAAK,EACf,KAAK,EAAE,QAAQ,CAAC,gBAAgB,EAChC,UAAU,EAAE,iBAAiB,EAC7B,aAAa,EAAE,QAAQ,CAAC,aAAa,EACrC,YAAY,EAAE,iBAAiB,EAC/B,YAAY,EACV,CAAA,MAAA,QAAQ,CAAC,IAAI,0CAAE,IAAI,MAAK,SAAS;gCAC/B,CAAC,CAAC,CAAC,MAAA,QAAQ,CAAC,IAAI,CAAC,YAAY,mCAAI,IAAI,CAAC;gCACtC,CAAC,CAAC,IAAI,EAEV,eAAe,EAAE,eAAe,aAAf,eAAe,cAAf,eAAe,GAAI,IAAI,EACxC,QAAQ,EAAE,QAAQ,CAAC,mBAAmB,GACtC,GACE,GACF,IACL,CACJ,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,CACL,8BACG,aAAa,EACd,KAAC,gBAAgB,KAAG,IACnB,CACJ,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,KAAK,KAAK,kBAAkB,IAAI,8BAA8B,EAAE,CAAC;QAC5E,qEAAqE;QACrE,6BAA6B;QAC7B,OAAO,CACL,8BACG,aAAa,EACd,KAAC,gBAAgB,KAAG,IACnB,CACJ,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,KAAK,KAAK,kBAAkB,EAAE,CAAC;QAC1C,OAAO,CACL,8BACG,aAAa,EACd,KAAC,cAAc,IAAC,SAAS,EAAE,QAAQ,CAAC,eAAgB,GAAI,IACvD,CACJ,CAAC;IACJ,CAAC;IACD,qFAAqF;IACrF,IAAI,QAAQ,CAAC,KAAK,KAAK,gBAAgB,IAAI,QAAQ,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;QACtE,OAAO,CACL,8BACG,aAAa,EACd,KAAC,GAAG,IAAC,SAAS,EAAC,kCAAkC,YAC/C,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,YACT,KAAC,KAAK,IACJ,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAE,SAAS,iBACH,uBAAuB,YAElC,QAAQ,CAAC,KAAK,IAAI,qCAAqC,GAClD,GACJ,GACF,IACL,CACJ,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,QAAQ,CAAC,cAAc,CAAC;IAE/C,OAAO,CACL,8BACG,aAAa,EACd,MAAC,GAAG,IAAC,SAAS,EAAC,kCAAkC,aAC/C,KAAC,uBAAuB,IACtB,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,OAAO,GAChB,EACF,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAC,WAAW,YAChC,aAAI,SAAS,EAAC,mCAAmC,yBAAc,GAC3D,EAEN,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,EAAE,YACrE,MAAC,GAAG,IAAC,SAAS,EAAC,uCAAuC,aAEpD,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,EAAC,kBAAkB,YACtD,0BACE,aAAI,SAAS,EAAC,0DAA0D,yBAEnE,EAEJ,cAAc,IAAI,CACjB,MAAC,KAAK,IACJ,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAE,SAAS,EACf,SAAS,EAAC,oCAAoC,iBAClC,qBAAqB,aAEjC,YAAG,SAAS,EAAC,MAAM,YAChB,aAAa,CACZ,CAAA,MAAA,QAAQ,CAAC,QAAQ,0CAAE,IAAI,MAAK,SAAS;4DACnC,CAAC,CAAC,QAAQ,CAAC,kBAAkB;4DAC7B,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAC/B,GACC,EACJ,eAAK,SAAS,EAAC,+DAA+D,aAC3E,eAAe,IAAI,CAClB,YAAG,IAAI,EAAE,eAAe,EAAE,SAAS,EAAC,gCAAgC,YACjE,aAAa,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GACvC,CACL,EACD,KAAC,MAAM,IAAC,OAAO,EAAC,OAAO,EAAC,SAAS,EAAC,cAAc,EAAC,OAAO,EAAE,iBAAiB,YACxE,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,GAC5B,IACL,IACA,CACT,EAEA,CAAC,CAAC,KAAK,IAAI,CACV,KAAC,KAAK,IACJ,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAE,SAAS,EACf,WAAW,QACX,OAAO,EAAE,CAAC,CAAC,UAAU,YAEpB,CAAC,CAAC,KAAK,GACF,CACT,EAEA,QAAQ,CAAC,eAAe,IAAI,CAC3B,KAAC,KAAK,IACJ,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAE,SAAS,EACf,WAAW,QACX,OAAO,EAAE,QAAQ,CAAC,oBAAoB,EACtC,SAAS,EAAC,MAAM,YAEf,QAAQ,CAAC,eAAe,GACnB,CACT,EAEA,CAAA,MAAA,QAAQ,CAAC,cAAc,0CAAE,OAAO,KAAI,CAAC,QAAQ,CAAC,eAAe,IAAI,CAChE,KAAC,KAAK,IACJ,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAE,SAAS,EACf,SAAS,EAAC,MAAM,iBACJ,uBAAuB,YAElC,6BAA6B,CAAC;oDAC7B,OAAO,EAAE,QAAQ,CAAC,cAAc,CAAC,OAAO;oDACxC,IAAI,EAAE,MAAA,QAAQ,CAAC,cAAc,CAAC,IAAI,mCAAI,SAAS;iDAChD,CAAC,GACI,CACT,EAEA,QAAQ,CAAC,WAAW,IAAI,CACvB,KAAC,KAAK,IACJ,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAE,SAAS,EACf,WAAW,QACX,OAAO,EAAE,QAAQ,CAAC,gBAAgB,EAClC,SAAS,EAAC,MAAM,iBACJ,oBAAoB,YAE/B,QAAQ,CAAC,WAAW,GACf,CACT,EAEA,CAAC,QAAQ,CAAC,aAAa,IAAI,eAAe,KAAI,MAAA,QAAQ,CAAC,IAAI,0CAAE,SAAS,CAAA,IAAI,CACzE,KAAC,KAAK,IAAC,OAAO,EAAC,MAAM,EAAC,SAAS,EAAC,MAAM,iBAAa,qBAAqB,YACrE,aAAa,CAAC,QAAQ,CAAC,kBAAkB,EAAE;oDAC1C,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;oDACjD,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ;iDAC3C,CAAC,GACI,CACT,EAEA,QAAQ,IAAI,CACX,8BACG,oBAAoB,IAAI,CACvB,KAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,YACtD,aAAa,CAAC,QAAQ,CAAC,gBAAgB,CAAC,GACnC,CACT,EACA,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CACvB,KAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,YACtD,aAAa;+DACX,aAAa,CAAC,QAAQ,CAAC,4BAA4B,CAAC,GACjD,CACT,EACA,iBAAiB,IAAI,eAAe,IAAI,oBAAoB,KAAK,iBAAiB,IAAI,CACrF,MAAC,KAAK,IACJ,OAAO,EAAC,SAAS,EACjB,IAAI,EAAE,WAAW,EACjB,WAAW,QACX,UAAU,EAAE,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,EAC3C,OAAO,EAAE,GAAG,EAAE,CAAC,uBAAuB,CAAC,iBAAiB,CAAC,EACzD,SAAS,EAAC,sBAAsB,iBACpB,sBAAsB,aAElC,8BAAkB,MAAM,8BAAW,EAClC,aAAa,CAAC,QAAQ,CAAC,kBAAkB,EAAE;gEAC1C,IAAI,EAAE,iBAAiB;gEACvB,OAAO,EAAE,eAAe,CAAC,OAAO;6DACjC,CAAC,IACI,CACT,EACD,KAAC,WAAW,IACV,OAAO,EAAE,CAAC,CAAC,OAAO,EAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ,EACpB,cAAc,EAAE,CAAC,CAAC,cAAc,EAChC,cAAc,EAAE,CAAC,CAAC,cAAc,EAChC,WAAW,EAAE,CAAC,CAAC,WAAW,EAC1B,SAAS,EAAE,CAAC,CAAC,SAAS,EACtB,eAAe,EAAE,CAAC,CAAC,eAAe,EAClC,WAAW,EAAE,CAAC,CAAC,WAAW,EAC1B,YAAY,EAAE,CAAC,CAAC,YAAY,EAC5B,UAAU,EACR,eAAe;4DACb,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,IAAI,QAAQ,CAAC,mBAAmB,CAAC;4DACzD,CAAC,CAAC,CAAC,CAAC,sBAAsB,EAE9B,YAAY,EACV,eAAe;4DACb,CAAC,CAAC,GAAG,EAAE;gEACH,QAAQ,CAAC,UAAU,CAAC;oEAClB,OAAO,kCACF,CAAC,CAAC,OAAO;wEACZ,+DAA+D;wEAC/D,4DAA4D;wEAC5D,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,WAAW,IAAI,EAAE,GAClD;oEACD,kBAAkB,EAAE,CAAC,CAAC,kBAAkB;iEACzC,CAAC,CAAC;4DACL,CAAC;4DACH,CAAC,CAAC,CAAC,CAAC,UAAU,EAElB,iBAAiB,EAAE,uBAAuB,EAC1C,QAAQ,EAAE,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EAC9D,WAAW,EAAE,IAAI,EACjB,WAAW,EAAE,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAC/D,WAAW,EAAE,CAAC,CAAC,WAAW,EAC1B,cAAc,EAAE,KAAK,CAAC,cAAc,EACpC,YAAY,EAAE,QAAQ,CAAC,IAAI,EAC3B,MAAM,EAAE,CAAC,CAAC,MAAM,EAChB,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC,WAAW,IAAI,IAAI,EAC1D,gBAAgB,EAAE,eAAe,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,SAAS,GACpE,IACD,CACJ,EAEA,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CACxB,KAAC,aAAa,IACZ,gBAAgB,EAAE,CAAA,MAAA,CAAC,CAAC,mBAAmB,0CAAE,IAAI,KAAI,EAAE,EACnD,SAAS,EAAE,CAAA,MAAA,CAAC,CAAC,mBAAmB,0CAAE,SAAS,KAAI,EAAE,EACjD,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,iBAAiB,GAC/B,CACH,EAEA,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CACxB,KAAC,aAAa,IAAC,MAAM,EAAE,CAAC,CAAC,WAAW,GAAI,CACzC,IACG,GACF,EAGN,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,EAAC,kBAAkB,YACrD,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CACvB,KAAC,iBAAiB,IAChB,MAAM,EAAE,CAAC,CAAC,MAAM,EAChB,aAAa,EAAE,iBAAiB,GAChC,CACH,CAAC,CAAC,CAAC,CACF,KAAC,WAAW,IACV,YAAY,EAAE,aAAa,EAC3B,aAAa,EAAE,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAChE,MAAM,EAAE,gBAAgB,EACxB,YAAY,EAAE,QAAQ,CAAC,IAAI,EAC3B,aAAa,EAAE,qBAAqB,EACpC,WAAW,EAAE,QAAQ,CAAC,gBAAgB,EACtC,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC;4CAC3B,KAAK,EAAE,cAAc;4CACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE;gDACjB,iBAAiB,CAAC,IAAI,CAAC,CAAC;gDACxB,IAAI,QAAQ,CAAC,WAAW;oDAAE,QAAQ,CAAC,gBAAgB,EAAE,CAAC;4CACxD,CAAC;4CACD,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,cAAc,CAAC;4CACnD,QAAQ,EAAE,GAAG,EAAE;gDACb,QAAQ,CAAC,YAAY,EAAE,CAAC;gDACxB,iBAAiB,CAAC,EAAE,CAAC,CAAC;gDACtB,kBAAkB,CAAC,IAAI,CAAC,CAAC;4CAC3B,CAAC;4CACD,WAAW,EAAE,iBAAiB;4CAC9B,QAAQ,EAAE,eAAe;4CACzB,KAAK,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI;4CAClD,KAAK,EAAE,QAAQ,CAAC,WAAW;4CAC3B,UAAU,EAAE,QAAQ,CAAC,cAAc;4CACnC,QAAQ,EAAE,QAAQ,CAAC,YAAY;yCAChC,CAAC,CAAC,CAAC,IAAI,GACR,CACH,GACG,IACF,GACF,IACF,IACL,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,QAAQ,CAAC","sourcesContent":["import React, { useCallback, useEffect, useState } from 'react';\nimport { Alert, Button, Col, Row } from '@openedx/paragon';\nimport { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';\nimport { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport AlacarteNotice from '../AlacarteNotice/AlacarteNotice';\nimport AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';\nimport AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel\nimport { getTrialDateRange } from '@src/utils';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { appliedDiscountPercent } from '../../utils/discount';\nimport { confirmPaymentWithMethodId, getLearnerFacingStripeMessage } from '../../utils/stripeCard';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle, CourseDetail } from '../../types';\nimport type { ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\n// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a\n// banner instead so the case is visible while the flow is being verified.\n// Flip back to true to restore the redirect.\nconst REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** Endpoint used to look up the representative IP for a selected country. */\n geolocationUrl?: string,\n /** Learner dashboard / order history URL for confirmation CTAs. */\n orderHistoryUrl?: string,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n orderHistoryUrl,\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });\n const c = useCheckout(props);\n const [promoCodeValue, setPromoCodeValue] = useState('');\n const [dismissedCouponAlert, setDismissedCouponAlert] = useState<string | null>(null);\n const [showValidationBanner, setShowValidationBanner] = useState(false);\n // Last code we successfully priced — drives the \"Save {percent}% with code\n // {code}\" hint on the empty field while a coupon is not yet applied.\n const [advertisedOffer, setAdvertisedOffer] = useState<{ code: string, percent: number } | null>(null);\n const isAlacarteReady = (alacarte.phase === 'ready' || alacarte.phase === 'alreadyOwned') && !!alacarte.checkout;\n const appliedCouponCode = alacarte.checkout?.couponCode?.trim()\n ? alacarte.checkout.couponCode.trim()\n : null;\n const discountPercent = appliedDiscountPercent(alacarte.line);\n // Only treat the line discount as a removable coupon when the backend says\n // so (or a coupon code is present). Automatic offers stay on CartSummary\n // via discountName and must not drive PromoCode's applied/remove UI.\n const isCouponDiscount = !!alacarte.line?.discount\n && alacarte.line.discountSource !== 'automatic'\n && (alacarte.line.discountSource === 'coupon' || !!appliedCouponCode);\n const appliedDiscount = isCouponDiscount && discountPercent != null\n ? {\n percent: discountPercent,\n amount: alacarte.line!.discount!.amount,\n currency: alacarte.line!.discount!.currency,\n }\n : null;\n\n // Keep the coupon field in sync when a code is applied via URL or API.\n // Don't overwrite the field with empty when only an automatic offer applies.\n useEffect(() => {\n if (alacarte.checkout?.couponCode) {\n setPromoCodeValue(alacarte.checkout.couponCode);\n }\n }, [alacarte.checkout?.couponCode]);\n\n useEffect(() => {\n if (appliedCouponCode && isCouponDiscount && discountPercent != null) {\n setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });\n }\n }, [appliedCouponCode, isCouponDiscount, discountPercent]);\n\n // No query params and no active cart — nothing to check out, send the\n // learner home rather than showing a placeholder.\n useEffect(() => {\n if (REDIRECT_ON_NO_ACTIVE_CHECKOUT && alacarte.phase === 'noActiveCheckout') {\n window.location.href = homeUrl;\n }\n }, [alacarte.phase, homeUrl]);\n\n // Express checkout (Apple Pay / Google Pay): runs the full place-order flow\n // using a wallet-provided PaymentMethod id instead of the mounted card element.\n const handleExpressPayment = useCallback(async (paymentMethodId: string, walletCountry?: string) => {\n if (!c.stripe) throw new Error('Stripe not loaded');\n const stripeInstance = c.stripe;\n\n await alacarte.placeOrderAsync({\n billing: {\n ...c.billing,\n country: c.billing.country || walletCountry || c.userCountry || '',\n },\n confirmCardPayment: async (clientSecret: string) => {\n await confirmPaymentWithMethodId({\n stripe: stripeInstance,\n clientSecret,\n paymentMethodId,\n });\n },\n });\n }, [alacarte, c.billing, c.userCountry, c.stripe]);\n\n const showProcessing\n = c.returningFromStripe\n && c.processingSubscription\n && c.phase !== 'success'\n && c.phase !== 'blocked'\n && c.phase !== 'failed';\n const showForm\n = !showProcessing\n && (c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing');\n const failedMessage = getLearnerFacingCheckoutMessage(c.lastComment);\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const isBillingCountryBlocked = GATE_CHECK_COUNTRY\n && !!c.billing.country\n && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);\n\n const selectedCycle: BillingCycle = c.selectedCycle;\n\n // Prefer the à la carte course's own detail (skills/badge/image, richer than\n // the subscription flow's ?course_run_key= lookup) when we selected a course.\n // When already-owned the line may be null, so fall back to the selector kind.\n const isCourseProduct = alacarte.line?.kind === 'course' || alacarte.selector?.kind === 'course';\n const isProgramProduct = alacarte.line?.kind === 'program' || alacarte.selector?.kind === 'program';\n\n const alacarteCourseDetail: CourseDetail | null\n = isCourseProduct && alacarte.productDetail && 'maId' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n const courseForSummary = alacarteCourseDetail ?? c.course;\n\n const alacarteProgramDetail: ProgramDetail | null\n = isProgramProduct && alacarte.productDetail && 'tCourses' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n\n const handleStartCourse = () => {\n const alacarteCourseUrl = alacarteCourseDetail?.tUrlCourse\n || (alacarte.productDetail && 'tUrlCourse' in alacarte.productDetail\n ? alacarte.productDetail.tUrlCourse\n : null);\n if (alacarteCourseUrl) {\n window.location.href = alacarteCourseUrl;\n } else if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = homeUrl || '/';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n if (showProcessing) {\n return <ProcessingScreen />;\n }\n\n // TEMPORARY — flip to true to dump à la carte hook state while validating\n // against the real backend. Remove AlacarteDebugPanel once the flow is solid.\n const SHOW_ALACARTE_DEBUG = false;\n const alacarteDebug = SHOW_ALACARTE_DEBUG\n ? <AlacarteDebugPanel data={alacarte} />\n : null;\n\n // Confirmation: show once the order is ready (or failed to load). While\n // tPlacedOrder is still polling, keep the checkout form so Pay now can show\n // \"Processing...\" instead of a full-page loader.\n if (alacarte.placedOrder || alacarte.placedOrderError) {\n const confirmationTitle = alacarte.line?.kind === 'program'\n ? alacarte.line.programName\n : alacarte.line?.kind === 'course'\n ? alacarte.line.courseName\n : null;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container alacarte-confirmation__container bg-light-200\">\n <Col xs={12}>\n <AlacarteConfirmation\n order={alacarte.placedOrder}\n awaiting={false}\n error={alacarte.placedOrderError}\n onContinue={handleStartCourse}\n productDetail={alacarte.productDetail}\n productTitle={confirmationTitle}\n ownedCourses={\n alacarte.line?.kind === 'program'\n ? (alacarte.line.ownedCourses ?? null)\n : null\n }\n orderHistoryUrl={orderHistoryUrl ?? null}\n placedAt={alacarte.placedOrderPlacedAt}\n />\n </Col>\n </Row>\n </>\n );\n }\n\n if (alacarte.phase === 'loading') {\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout' && REDIRECT_ON_NO_ACTIVE_CHECKOUT) {\n // Redirects home via the effect above — show a spinner rather than a\n // notice while that happens.\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout') {\n return (\n <>\n {alacarteDebug}\n <AlacarteNotice messageId={alacarte.noticeMessageId!} />\n </>\n );\n }\n // Product unavailable / not found / not purchasable — banner only, no checkout form.\n if (alacarte.phase === 'notPurchasable' || alacarte.phase === 'error') {\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n data-testid=\"alacarte-error-banner\"\n >\n {alacarte.error || 'Something went wrong with checkout.'}\n </Alert>\n </Col>\n </Row>\n </>\n );\n }\n\n const isAlreadyOwned = alacarte.isAlreadyOwned;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <CountryUnavailableModal\n isOpen={isBillingCountryBlocked}\n homeUrl={homeUrl}\n />\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n\n <Col xs={12} className={isAlreadyOwned ? 'checkout__owned-overlay' : ''}>\n <Row className=\"flex-column-reverse gap-5 flex-lg-row\">\n {/* ── LEFT ── */}\n <Col xs={12} lg={7} xxl={9} className=\"payment__options\">\n <div>\n <h1 className=\"font-weight-bold text-primary-900 d-none d-lg-block mb-4\">\n Checkout\n </h1>\n\n {isAlreadyOwned && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"checkout__already-owned-alert mb-3\"\n data-testid=\"already-owned-alert\"\n >\n <p className=\"mb-3\">\n {formatMessage(\n alacarte.selector?.kind === 'program'\n ? messages.alreadyPaidProgram\n : messages.alreadyPaidCourse,\n )}\n </p>\n <div className=\"d-flex align-items-center justify-content-end gap-3 flex-wrap\">\n {orderHistoryUrl && (\n <a href={orderHistoryUrl} className=\"text-gray-900 font-weight-bold\">\n {formatMessage(messages.viewOrderHistory)}\n </a>\n )}\n <Button variant=\"brand\" className=\"rounded-pill\" onClick={handleStartCourse}>\n {formatMessage(messages.goToCourse)}\n </Button>\n </div>\n </Alert>\n )}\n\n {c.error && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={c.clearError}\n >\n {c.error}\n </Alert>\n )}\n\n {alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearPlaceOrderError}\n className=\"mb-3\"\n >\n {alacarte.placeOrderError}\n </Alert>\n )}\n\n {alacarte.paymentDecline?.message && !alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"mb-3\"\n data-testid=\"payment-decline-alert\"\n >\n {getLearnerFacingStripeMessage({\n message: alacarte.paymentDecline.message,\n code: alacarte.paymentDecline.code ?? undefined,\n })}\n </Alert>\n )}\n\n {alacarte.couponError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearCouponError}\n className=\"mb-3\"\n data-testid=\"coupon-error-alert\"\n >\n {alacarte.couponError}\n </Alert>\n )}\n\n {!alacarte.priceAccepted && isAlacarteReady && alacarte.line?.amountDue && (\n <Alert variant=\"info\" className=\"mb-3\" data-testid=\"price-accept-notice\">\n {formatMessage(messages.priceChangedNotice, {\n amount: alacarte.line.amountDue.amount.toFixed(2),\n currency: alacarte.line.amountDue.currency,\n })}\n </Alert>\n )}\n\n {showForm && (\n <>\n {showValidationBanner && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {formatMessage(messages.validationBanner)}\n </Alert>\n )}\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || formatMessage(messages.paymentProcessingDescription)}\n </Alert>\n )}\n {appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={() => setDismissedCouponAlert(appliedCouponCode)}\n className=\"coupon-applied-alert\"\n data-testid=\"coupon-applied-alert\"\n >\n <span aria-hidden=\"true\">🎉 </span>\n {formatMessage(messages.couponAppliedAlert, {\n code: appliedCouponCode,\n percent: appliedDiscount.percent,\n })}\n </Alert>\n )}\n <PaymentForm\n billing={c.billing}\n setField={c.setField}\n numberMountRef={c.numberMountRef}\n expiryMountRef={c.expiryMountRef}\n cvcMountRef={c.cvcMountRef}\n cardError={c.cardError}\n cardFieldStates={c.cardFieldStates}\n stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={\n isAlacarteReady\n ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)\n : c.processingSubscription\n }\n onPlaceOrder={\n isAlacarteReady\n ? () => {\n alacarte.placeOrder({\n billing: {\n ...c.billing,\n // Country is required on tCheckoutPlaceOrder (two-letter ISO).\n // Prefer the form selection; fall back to gate geolocation.\n country: c.billing.country || c.userCountry || '',\n },\n confirmCardPayment: c.confirmCardPayment,\n });\n }\n : c.placeOrder\n }\n onValidationError={setShowValidationBanner}\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={null}\n cycleToShow={selectedCycle === 'monthly' ? 'monthly' : 'annual'}\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n alacarteLine={alacarte.line}\n stripe={c.stripe}\n expressCountry={c.billing.country || c.userCountry || 'US'}\n onExpressPayment={isAlacarteReady ? handleExpressPayment : undefined}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n courseLabel={courseLabel}\n onFindCourse={handleStartCourse}\n />\n )}\n\n {c.phase === 'blocked' && (\n <BlockedScreen detail={c.lastComment} />\n )}\n </div>\n </Col>\n\n {/* ── RIGHT — swaps to EnrollmentSummary on success ── */}\n <Col xs={12} lg={5} xxl={3} className=\"payment__summary\">\n {c.phase === 'success' ? (\n <EnrollmentSummary\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={courseForSummary}\n alacarteLine={alacarte.line}\n programDetail={alacarteProgramDetail}\n programName={alacarte.ownedProductName}\n promoCode={isAlacarteReady ? {\n value: promoCodeValue,\n onChange: (next) => {\n setPromoCodeValue(next);\n if (alacarte.couponError) alacarte.clearCouponError();\n },\n onApply: () => alacarte.applyCoupon(promoCodeValue),\n onRemove: () => {\n alacarte.removeCoupon();\n setPromoCodeValue('');\n setAdvertisedOffer(null);\n },\n appliedCode: appliedCouponCode,\n discount: appliedDiscount,\n offer: !appliedCouponCode ? advertisedOffer : null,\n error: alacarte.couponError,\n isApplying: alacarte.couponApplying,\n disabled: alacarte.placingOrder,\n } : null}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n </>\n );\n};\n\nexport default Checkout;\n"]}
1
+ {"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useState } from 'react';\nimport { Alert, Button, Col, Row } from '@openedx/paragon';\nimport { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';\nimport { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport AlacarteNotice from '../AlacarteNotice/AlacarteNotice';\nimport AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';\nimport AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel\nimport { getTrialDateRange } from '@src/utils';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { appliedDiscountPercent } from '../../utils/discount';\nimport { confirmPaymentWithMethodId, getLearnerFacingStripeMessage } from '../../utils/stripeCard';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle, CourseDetail } from '../../types';\nimport type { ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\n// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a\n// banner instead so the case is visible while the flow is being verified.\n// Flip back to true to restore the redirect.\nconst REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** Endpoint used to look up the representative IP for a selected country. */\n geolocationUrl?: string,\n /** Learner dashboard / order history URL for confirmation CTAs. */\n orderHistoryUrl?: string,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n orderHistoryUrl,\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });\n const c = useCheckout(props);\n const [promoCodeValue, setPromoCodeValue] = useState('');\n const [dismissedCouponAlert, setDismissedCouponAlert] = useState<string | null>(null);\n const [showValidationBanner, setShowValidationBanner] = useState(false);\n // Last code we successfully priced — drives the \"Save {percent}% with code\n // {code}\" hint on the empty field while a coupon is not yet applied.\n const [advertisedOffer, setAdvertisedOffer] = useState<{ code: string, percent: number } | null>(null);\n const isAlacarteReady = (alacarte.phase === 'ready' || alacarte.phase === 'alreadyOwned') && !!alacarte.checkout;\n const appliedCouponCode = alacarte.checkout?.couponCode?.trim()\n ? alacarte.checkout.couponCode.trim()\n : null;\n const discountPercent = appliedDiscountPercent(alacarte.line);\n // Only treat the line discount as a removable coupon when the backend says\n // so (or a coupon code is present). Automatic offers stay on CartSummary\n // via discountName and must not drive PromoCode's applied/remove UI.\n const isCouponDiscount = !!alacarte.line?.discount\n && alacarte.line.discountSource !== 'automatic'\n && (alacarte.line.discountSource === 'coupon' || !!appliedCouponCode);\n const appliedDiscount = isCouponDiscount && discountPercent != null\n ? {\n percent: discountPercent,\n amount: alacarte.line!.discount!.amount,\n currency: alacarte.line!.discount!.currency,\n }\n : null;\n\n // Keep the coupon field in sync when a code is applied via URL or API.\n // Don't overwrite the field with empty when only an automatic offer applies.\n useEffect(() => {\n if (alacarte.checkout?.couponCode) {\n setPromoCodeValue(alacarte.checkout.couponCode);\n }\n }, [alacarte.checkout?.couponCode]);\n\n useEffect(() => {\n if (appliedCouponCode && isCouponDiscount && discountPercent != null) {\n setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });\n }\n }, [appliedCouponCode, isCouponDiscount, discountPercent]);\n\n // No query params and no active cart — nothing to check out, send the\n // learner home rather than showing a placeholder.\n useEffect(() => {\n if (REDIRECT_ON_NO_ACTIVE_CHECKOUT && alacarte.phase === 'noActiveCheckout') {\n window.location.href = homeUrl;\n }\n }, [alacarte.phase, homeUrl]);\n\n // Express checkout (Apple Pay / Google Pay): runs the full place-order flow\n // using a wallet-provided PaymentMethod id instead of the mounted card element.\n const handleExpressPayment = useCallback(async (paymentMethodId: string, walletCountry?: string) => {\n if (!c.stripe) throw new Error('Stripe not loaded');\n const stripeInstance = c.stripe;\n const country = (c.billing.country || walletCountry || c.userCountry || '').trim().toUpperCase();\n if (!country) {\n throw new Error('Please select a country before using Apple Pay or Google Pay.');\n }\n\n await alacarte.placeOrderAsync({\n billing: {\n ...c.billing,\n country,\n },\n confirmCardPayment: async (clientSecret: string) => {\n await confirmPaymentWithMethodId({\n stripe: stripeInstance,\n clientSecret,\n paymentMethodId,\n });\n },\n });\n }, [alacarte, c.billing, c.userCountry, c.stripe]);\n\n const showProcessing\n = c.returningFromStripe\n && c.processingSubscription\n && c.phase !== 'success'\n && c.phase !== 'blocked'\n && c.phase !== 'failed';\n const showForm\n = !showProcessing\n && (c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing');\n const failedMessage = getLearnerFacingCheckoutMessage(c.lastComment);\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const isBillingCountryBlocked = GATE_CHECK_COUNTRY\n && !!c.billing.country\n && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);\n\n const selectedCycle: BillingCycle = c.selectedCycle;\n\n // Prefer the à la carte course's own detail (skills/badge/image, richer than\n // the subscription flow's ?course_run_key= lookup) when we selected a course.\n // When already-owned the line may be null, so fall back to the selector kind.\n const isCourseProduct = alacarte.line?.kind === 'course' || alacarte.selector?.kind === 'course';\n const isProgramProduct = alacarte.line?.kind === 'program' || alacarte.selector?.kind === 'program';\n\n const alacarteCourseDetail: CourseDetail | null\n = isCourseProduct && alacarte.productDetail && 'maId' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n const courseForSummary = alacarteCourseDetail ?? c.course;\n\n const alacarteProgramDetail: ProgramDetail | null\n = isProgramProduct && alacarte.productDetail && 'tCourses' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n\n const handleStartCourse = () => {\n const alacarteCourseUrl = alacarteCourseDetail?.tUrlCourse\n || (alacarte.productDetail && 'tUrlCourse' in alacarte.productDetail\n ? alacarte.productDetail.tUrlCourse\n : null);\n if (alacarteCourseUrl) {\n window.location.href = alacarteCourseUrl;\n } else if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = homeUrl || '/';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n if (showProcessing) {\n return <ProcessingScreen />;\n }\n\n // TEMPORARY — flip to true to dump à la carte hook state while validating\n // against the real backend. Remove AlacarteDebugPanel once the flow is solid.\n const SHOW_ALACARTE_DEBUG = false;\n const alacarteDebug = SHOW_ALACARTE_DEBUG\n ? <AlacarteDebugPanel data={alacarte} />\n : null;\n\n // Confirmation: show once the order is ready (or failed to load). While\n // tPlacedOrder is still polling, keep the checkout form so Pay now can show\n // \"Processing...\" instead of a full-page loader.\n if (alacarte.placedOrder || alacarte.placedOrderError) {\n const confirmationTitle = alacarte.line?.kind === 'program'\n ? alacarte.line.programName\n : alacarte.line?.kind === 'course'\n ? alacarte.line.courseName\n : null;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container alacarte-confirmation__container bg-light-200\">\n <Col xs={12}>\n <AlacarteConfirmation\n order={alacarte.placedOrder}\n awaiting={false}\n error={alacarte.placedOrderError}\n onContinue={handleStartCourse}\n productDetail={alacarte.productDetail}\n productTitle={confirmationTitle}\n ownedCourses={\n alacarte.line?.kind === 'program'\n ? (alacarte.line.ownedCourses ?? null)\n : null\n }\n orderHistoryUrl={orderHistoryUrl ?? null}\n placedAt={alacarte.placedOrderPlacedAt}\n />\n </Col>\n </Row>\n </>\n );\n }\n\n if (alacarte.phase === 'loading') {\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout' && REDIRECT_ON_NO_ACTIVE_CHECKOUT) {\n // Redirects home via the effect above — show a spinner rather than a\n // notice while that happens.\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout') {\n return (\n <>\n {alacarteDebug}\n <AlacarteNotice messageId={alacarte.noticeMessageId!} />\n </>\n );\n }\n // Product unavailable / not found / not purchasable — banner only, no checkout form.\n if (alacarte.phase === 'notPurchasable' || alacarte.phase === 'error') {\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n data-testid=\"alacarte-error-banner\"\n >\n {alacarte.error || 'Something went wrong with checkout.'}\n </Alert>\n </Col>\n </Row>\n </>\n );\n }\n\n const isAlreadyOwned = alacarte.isAlreadyOwned;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <CountryUnavailableModal\n isOpen={isBillingCountryBlocked}\n homeUrl={homeUrl}\n />\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n\n <Col xs={12} className={isAlreadyOwned ? 'checkout__owned-overlay' : ''}>\n <Row className=\"flex-column-reverse gap-5 flex-lg-row\">\n {/* ── LEFT ── */}\n <Col xs={12} lg={7} xxl={9} className=\"payment__options\">\n <div>\n <h1 className=\"font-weight-bold text-primary-900 d-none d-lg-block mb-4\">\n Checkout\n </h1>\n\n {isAlreadyOwned && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"checkout__already-owned-alert mb-3\"\n data-testid=\"already-owned-alert\"\n >\n <p className=\"mb-3\">\n {formatMessage(\n alacarte.selector?.kind === 'program'\n ? messages.alreadyPaidProgram\n : messages.alreadyPaidCourse,\n )}\n </p>\n <div className=\"d-flex align-items-center justify-content-end gap-3 flex-wrap\">\n {orderHistoryUrl && (\n <a href={orderHistoryUrl} className=\"text-gray-900 font-weight-bold\">\n {formatMessage(messages.viewOrderHistory)}\n </a>\n )}\n <Button variant=\"brand\" className=\"rounded-pill\" onClick={handleStartCourse}>\n {formatMessage(messages.goToCourse)}\n </Button>\n </div>\n </Alert>\n )}\n\n {c.error && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={c.clearError}\n >\n {c.error}\n </Alert>\n )}\n\n {alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearPlaceOrderError}\n className=\"mb-3\"\n >\n {alacarte.placeOrderError}\n </Alert>\n )}\n\n {alacarte.paymentDecline?.message && !alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"mb-3\"\n data-testid=\"payment-decline-alert\"\n >\n {getLearnerFacingStripeMessage({\n message: alacarte.paymentDecline.message,\n code: alacarte.paymentDecline.code ?? undefined,\n })}\n </Alert>\n )}\n\n {alacarte.couponError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearCouponError}\n className=\"mb-3\"\n data-testid=\"coupon-error-alert\"\n >\n {alacarte.couponError}\n </Alert>\n )}\n\n {!alacarte.priceAccepted && isAlacarteReady && alacarte.line?.amountDue && (\n <Alert variant=\"info\" className=\"mb-3\" data-testid=\"price-accept-notice\">\n {formatMessage(messages.priceChangedNotice, {\n amount: alacarte.line.amountDue.amount.toFixed(2),\n currency: alacarte.line.amountDue.currency,\n })}\n </Alert>\n )}\n\n {showForm && (\n <>\n {showValidationBanner && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {formatMessage(messages.validationBanner)}\n </Alert>\n )}\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || formatMessage(messages.paymentProcessingDescription)}\n </Alert>\n )}\n {appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={() => setDismissedCouponAlert(appliedCouponCode)}\n className=\"coupon-applied-alert\"\n data-testid=\"coupon-applied-alert\"\n >\n <span aria-hidden=\"true\">🎉 </span>\n {formatMessage(messages.couponAppliedAlert, {\n code: appliedCouponCode,\n percent: appliedDiscount.percent,\n })}\n </Alert>\n )}\n <PaymentForm\n billing={c.billing}\n setField={c.setField}\n numberMountRef={c.numberMountRef}\n expiryMountRef={c.expiryMountRef}\n cvcMountRef={c.cvcMountRef}\n cardError={c.cardError}\n cardFieldStates={c.cardFieldStates}\n stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={\n isAlacarteReady\n ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)\n : c.processingSubscription\n }\n onPlaceOrder={\n isAlacarteReady\n ? () => {\n alacarte.placeOrder({\n billing: {\n ...c.billing,\n // Country is required on tCheckoutPlaceOrder (two-letter ISO).\n // Prefer the form selection; fall back to gate geolocation.\n country: c.billing.country || c.userCountry || '',\n },\n confirmCardPayment: c.confirmCardPayment,\n });\n }\n : c.placeOrder\n }\n onValidationError={setShowValidationBanner}\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={null}\n cycleToShow={selectedCycle === 'monthly' ? 'monthly' : 'annual'}\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n alacarteLine={alacarte.line}\n stripe={c.stripe}\n expressCountry={c.billing.country || c.userCountry || 'US'}\n onExpressPayment={isAlacarteReady ? handleExpressPayment : undefined}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n courseLabel={courseLabel}\n onFindCourse={handleStartCourse}\n />\n )}\n\n {c.phase === 'blocked' && (\n <BlockedScreen detail={c.lastComment} />\n )}\n </div>\n </Col>\n\n {/* ── RIGHT — swaps to EnrollmentSummary on success ── */}\n <Col xs={12} lg={5} xxl={3} className=\"payment__summary\">\n {c.phase === 'success' ? (\n <EnrollmentSummary\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={courseForSummary}\n alacarteLine={alacarte.line}\n programDetail={alacarteProgramDetail}\n programName={alacarte.ownedProductName}\n promoCode={isAlacarteReady ? {\n value: promoCodeValue,\n onChange: (next) => {\n setPromoCodeValue(next);\n if (alacarte.couponError) alacarte.clearCouponError();\n },\n onApply: () => alacarte.applyCoupon(promoCodeValue),\n onRemove: () => {\n alacarte.removeCoupon();\n setPromoCodeValue('');\n setAdvertisedOffer(null);\n },\n appliedCode: appliedCouponCode,\n discount: appliedDiscount,\n offer: !appliedCouponCode ? advertisedOffer : null,\n error: alacarte.couponError,\n isApplying: alacarte.couponApplying,\n disabled: alacarte.placingOrder,\n } : null}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n </>\n );\n};\n\nexport default Checkout;\n"]}
@@ -4,8 +4,6 @@ export interface CheckoutGateProps extends CheckoutProps {
4
4
  learnerDashboardUrl?: string;
5
5
  /** Homepage URL for the country-unavailable modal CTA. */
6
6
  homeUrl?: string;
7
- /** ALC-307 compliance-check endpoint — decides whether the learner is embargoed. */
8
- complianceCheckUrl?: string;
9
7
  }
10
- declare const CheckoutGate: ({ learnerDashboardUrl, homeUrl, complianceCheckUrl, ...checkoutProps }: CheckoutGateProps) => import("react/jsx-runtime").JSX.Element;
8
+ declare const CheckoutGate: ({ learnerDashboardUrl, homeUrl, ...checkoutProps }: CheckoutGateProps) => import("react/jsx-runtime").JSX.Element;
11
9
  export default CheckoutGate;
@@ -18,13 +18,11 @@ import Checkout from '../Checkout/Checkout';
18
18
  import CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';
19
19
  import messages from './Messages';
20
20
  const CheckoutGate = (_a) => {
21
- var { learnerDashboardUrl, homeUrl = '/', complianceCheckUrl } = _a, checkoutProps = __rest(_a, ["learnerDashboardUrl", "homeUrl", "complianceCheckUrl"]);
21
+ var { learnerDashboardUrl, homeUrl = '/' } = _a, checkoutProps = __rest(_a, ["learnerDashboardUrl", "homeUrl"]);
22
22
  const { formatMessage } = useIntl();
23
23
  const { gateStatus, userCountry, gateMessage } = useCheckoutGate({
24
24
  apiUrl: checkoutProps.apiUrl,
25
25
  edxSessionUrl: checkoutProps.edxSessionUrl,
26
- geolocationUrl: checkoutProps.geolocationUrl,
27
- complianceCheckUrl,
28
26
  learnerDashboardUrl,
29
27
  });
30
28
  if (gateStatus === 'loading') {
@@ -1 +1 @@
1
- {"version":3,"file":"CheckoutGate.js","sourceRoot":"","sources":["../../../src/components/CheckoutGate/CheckoutGate.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,QAAQ,MAAM,sBAAsB,CAAC;AAC5C,OAAO,uBAAuB,MAAM,oDAAoD,CAAC;AAEzF,OAAO,QAAQ,MAAM,YAAY,CAAC;AAWlC,MAAM,YAAY,GAAG,CAAC,EAKF,EAAE,EAAE;QALF,EACpB,mBAAmB,EACnB,OAAO,GAAG,GAAG,EACb,kBAAkB,OAEA,EADf,aAAa,cAJI,wDAKrB,CADiB;IAEhB,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,eAAe,CAAC;QAC/D,MAAM,EAAE,aAAa,CAAC,MAAM;QAC5B,aAAa,EAAE,aAAa,CAAC,aAAa;QAC1C,cAAc,EAAE,aAAa,CAAC,cAAc;QAC5C,kBAAkB;QAClB,mBAAmB;KACpB,CAAC,CAAC;IAEH,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CACL,cAAK,SAAS,EAAC,mBAAmB,EAAC,IAAI,EAAC,QAAQ,eAAW,QAAQ,YACjE,KAAC,OAAO,IAAC,SAAS,EAAC,QAAQ,EAAC,gBAAgB,EAAC,6BAAwB,GAAG,GACpE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,CACL,MAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,EAAE,SAAS,aACpC,WAAW,aAAX,WAAW,cAAX,WAAW,GACT,aAAa,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,GAAG,EAClD,mBAAmB,IAAI,CACtB,YAAG,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAC,YAAY,6CAEhD,CACL,IACK,CACT,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CACL,4BACE,KAAC,uBAAuB,IAAC,MAAM,QAAC,OAAO,EAAE,OAAO,GAAI,GACnD,CACJ,CAAC;IACJ,CAAC;IAED,OAAO,CACL,KAAC,QAAQ,oBACH,aAAa,IACjB,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,EAChB,eAAe,EAAE,mBAAmB,IACpC,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,YAAY,CAAC","sourcesContent":["import { Alert, Spinner } from '@openedx/paragon';\nimport { Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { useCheckoutGate } from '../../data/hooks/useCheckoutGate';\nimport Checkout from '../Checkout/Checkout';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport type { CheckoutProps } from '../Checkout/Checkout';\nimport messages from './Messages';\n\nexport interface CheckoutGateProps extends CheckoutProps {\n /** URL linked in session-error recovery messaging. */\n learnerDashboardUrl?: string,\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** ALC-307 compliance-check endpoint — decides whether the learner is embargoed. */\n complianceCheckUrl?: string,\n}\n\nconst CheckoutGate = ({\n learnerDashboardUrl,\n homeUrl = '/',\n complianceCheckUrl,\n ...checkoutProps\n}: CheckoutGateProps) => {\n const { formatMessage } = useIntl();\n const { gateStatus, userCountry, gateMessage } = useCheckoutGate({\n apiUrl: checkoutProps.apiUrl,\n edxSessionUrl: checkoutProps.edxSessionUrl,\n geolocationUrl: checkoutProps.geolocationUrl,\n complianceCheckUrl,\n learnerDashboardUrl,\n });\n\n if (gateStatus === 'loading') {\n return (\n <div className=\"processing-screen\" role=\"status\" aria-live=\"polite\">\n <Spinner animation=\"border\" screenReaderText=\"Checking your account…\" />\n </div>\n );\n }\n\n if (gateStatus === 'error') {\n return (\n <Alert variant=\"danger\" icon={ErrorIcon}>\n {gateMessage\n ?? formatMessage(messages.defaultErrorMessage)}{' '}\n {learnerDashboardUrl && (\n <a href={learnerDashboardUrl} className=\"alert-link\">\n Go to your learner dashboard\n </a>\n )}\n </Alert>\n );\n }\n\n if (gateStatus === 'blocked') {\n return (\n <>\n <CountryUnavailableModal isOpen homeUrl={homeUrl} />\n </>\n );\n }\n\n return (\n <Checkout\n {...checkoutProps}\n userCountry={userCountry}\n homeUrl={homeUrl}\n orderHistoryUrl={learnerDashboardUrl}\n />\n );\n};\n\nexport default CheckoutGate;\n"]}
1
+ {"version":3,"file":"CheckoutGate.js","sourceRoot":"","sources":["../../../src/components/CheckoutGate/CheckoutGate.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,QAAQ,MAAM,sBAAsB,CAAC;AAC5C,OAAO,uBAAuB,MAAM,oDAAoD,CAAC;AAEzF,OAAO,QAAQ,MAAM,YAAY,CAAC;AASlC,MAAM,YAAY,GAAG,CAAC,EAIF,EAAE,EAAE;QAJF,EACpB,mBAAmB,EACnB,OAAO,GAAG,GAAG,OAEK,EADf,aAAa,cAHI,kCAIrB,CADiB;IAEhB,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,eAAe,CAAC;QAC/D,MAAM,EAAE,aAAa,CAAC,MAAM;QAC5B,aAAa,EAAE,aAAa,CAAC,aAAa;QAC1C,mBAAmB;KACpB,CAAC,CAAC;IAEH,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CACL,cAAK,SAAS,EAAC,mBAAmB,EAAC,IAAI,EAAC,QAAQ,eAAW,QAAQ,YACjE,KAAC,OAAO,IAAC,SAAS,EAAC,QAAQ,EAAC,gBAAgB,EAAC,6BAAwB,GAAG,GACpE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,CACL,MAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,EAAE,SAAS,aACpC,WAAW,aAAX,WAAW,cAAX,WAAW,GACT,aAAa,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,GAAG,EAClD,mBAAmB,IAAI,CACtB,YAAG,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAC,YAAY,6CAEhD,CACL,IACK,CACT,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CACL,4BACE,KAAC,uBAAuB,IAAC,MAAM,QAAC,OAAO,EAAE,OAAO,GAAI,GACnD,CACJ,CAAC;IACJ,CAAC;IAED,OAAO,CACL,KAAC,QAAQ,oBACH,aAAa,IACjB,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,EAChB,eAAe,EAAE,mBAAmB,IACpC,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,YAAY,CAAC","sourcesContent":["import { Alert, Spinner } from '@openedx/paragon';\nimport { Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { useCheckoutGate } from '../../data/hooks/useCheckoutGate';\nimport Checkout from '../Checkout/Checkout';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport type { CheckoutProps } from '../Checkout/Checkout';\nimport messages from './Messages';\n\nexport interface CheckoutGateProps extends CheckoutProps {\n /** URL linked in session-error recovery messaging. */\n learnerDashboardUrl?: string,\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n}\n\nconst CheckoutGate = ({\n learnerDashboardUrl,\n homeUrl = '/',\n ...checkoutProps\n}: CheckoutGateProps) => {\n const { formatMessage } = useIntl();\n const { gateStatus, userCountry, gateMessage } = useCheckoutGate({\n apiUrl: checkoutProps.apiUrl,\n edxSessionUrl: checkoutProps.edxSessionUrl,\n learnerDashboardUrl,\n });\n\n if (gateStatus === 'loading') {\n return (\n <div className=\"processing-screen\" role=\"status\" aria-live=\"polite\">\n <Spinner animation=\"border\" screenReaderText=\"Checking your account…\" />\n </div>\n );\n }\n\n if (gateStatus === 'error') {\n return (\n <Alert variant=\"danger\" icon={ErrorIcon}>\n {gateMessage\n ?? formatMessage(messages.defaultErrorMessage)}{' '}\n {learnerDashboardUrl && (\n <a href={learnerDashboardUrl} className=\"alert-link\">\n Go to your learner dashboard\n </a>\n )}\n </Alert>\n );\n }\n\n if (gateStatus === 'blocked') {\n return (\n <>\n <CountryUnavailableModal isOpen homeUrl={homeUrl} />\n </>\n );\n }\n\n return (\n <Checkout\n {...checkoutProps}\n userCountry={userCountry}\n homeUrl={homeUrl}\n orderHistoryUrl={learnerDashboardUrl}\n />\n );\n};\n\nexport default CheckoutGate;\n"]}
@@ -7,7 +7,7 @@ export interface ExpressCheckoutProps {
7
7
  totalAmountCents: number | null;
8
8
  /** ISO 4217 currency code, e.g. 'USD'. */
9
9
  currency: string | null;
10
- /** Two-letter country code for Payment Request initialisation. */
10
+ /** Two-letter country code (kept for API stability). */
11
11
  country: string;
12
12
  /**
13
13
  * Called when the user authorises a wallet payment (Apple Pay / Google Pay).
@@ -17,5 +17,13 @@ export interface ExpressCheckoutProps {
17
17
  onPaymentAuthorized: (paymentMethodId: string, walletCountry?: string) => Promise<void>;
18
18
  disabled?: boolean;
19
19
  }
20
+ /**
21
+ * Express checkout: PayPal (local) | Apple Pay | Google Pay.
22
+ *
23
+ * Apple and Google each get their own Express Checkout Element (one wallet per
24
+ * mount) so we can lay them out in equal columns. A single ECE with both wallets
25
+ * stacks / overflows in this narrow column. `always` keeps the Shopify-like
26
+ * show-even-without-a-saved-card behaviour.
27
+ */
20
28
  declare const ExpressCheckout: React.FC<ExpressCheckoutProps>;
21
29
  export default ExpressCheckout;