@edx/frontend-app-subscription-checkout 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +2 -6
- package/dist/app.js.map +1 -1
- package/dist/checkoutPage/CheckoutPage.js +1 -2
- package/dist/checkoutPage/CheckoutPage.js.map +1 -1
- package/dist/components/BrandMarks/BrandMarks.d.ts +5 -1
- package/dist/components/BrandMarks/BrandMarks.js +2 -1
- package/dist/components/BrandMarks/BrandMarks.js.map +1 -1
- package/dist/components/CartSummary/CartSummary.d.ts +2 -7
- package/dist/components/CartSummary/CartSummary.js +12 -8
- package/dist/components/CartSummary/CartSummary.js.map +1 -1
- package/dist/components/CartSummary/Messages.d.ts +15 -0
- package/dist/components/CartSummary/Messages.js +15 -0
- package/dist/components/CartSummary/Messages.js.map +1 -1
- package/dist/components/Checkout/Checkout.d.ts +1 -6
- package/dist/components/Checkout/Checkout.js +4 -13
- package/dist/components/Checkout/Checkout.js.map +1 -1
- package/dist/components/EnrollmentSummary/EnrollmentSummary.d.ts +7 -1
- package/dist/components/EnrollmentSummary/EnrollmentSummary.js +10 -6
- package/dist/components/EnrollmentSummary/EnrollmentSummary.js.map +1 -1
- package/dist/components/EnrollmentSummary/Messages.d.ts +10 -0
- package/dist/components/EnrollmentSummary/Messages.js +10 -0
- package/dist/components/EnrollmentSummary/Messages.js.map +1 -1
- package/dist/components/PaymentForm/Messages.d.ts +16 -1
- package/dist/components/PaymentForm/Messages.js +20 -5
- package/dist/components/PaymentForm/Messages.js.map +1 -1
- package/dist/components/PaymentForm/PaymentForm.d.ts +7 -1
- package/dist/components/PaymentForm/PaymentForm.js +33 -22
- package/dist/components/PaymentForm/PaymentForm.js.map +1 -1
- package/dist/data/hooks/useCheckout.js +7 -2
- package/dist/data/hooks/useCheckout.js.map +1 -1
- package/dist/styles/components/brandMarks.scss +9 -22
- package/dist/styles/components/cartSummary.scss +7 -0
- package/dist/styles/components/enrollmentSummary.scss +6 -0
- package/dist/styles/components/paymentForm.scss +20 -0
- package/dist/utils/planQuery.d.ts +16 -0
- package/dist/utils/planQuery.js +57 -0
- package/dist/utils/planQuery.js.map +1 -0
- package/dist/utils/provision.js +2 -2
- package/dist/utils/provision.js.map +1 -1
- package/package.json +1 -1
|
@@ -110,6 +110,26 @@
|
|
|
110
110
|
&__terms {
|
|
111
111
|
line-height: 1.6;
|
|
112
112
|
}
|
|
113
|
+
|
|
114
|
+
&__confirm {
|
|
115
|
+
.pgn__form-checkbox {
|
|
116
|
+
align-items: flex-start;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Paragon pins the label line-height to the checkbox size; let the
|
|
120
|
+
// copy wrap with its own leading instead.
|
|
121
|
+
.pgn__form-label {
|
|
122
|
+
display: block;
|
|
123
|
+
line-height: 1.6;
|
|
124
|
+
// Cancel half-leading above the first line and below the last so
|
|
125
|
+
// extra space only appears between wrapped lines.
|
|
126
|
+
margin-block: calc((1em - 1lh) / 2);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
&__confirm-label {
|
|
131
|
+
line-height: inherit;
|
|
132
|
+
}
|
|
113
133
|
}
|
|
114
134
|
|
|
115
135
|
@media (--pgn-size-breakpoint-min-width-lg) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BillingCycle } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Map ?plan= values to the internal billing cycle.
|
|
4
|
+
* Accepts monthly, annual, and annually (the public query value).
|
|
5
|
+
*/
|
|
6
|
+
export declare function parsePlanParam(raw: string | null | undefined): BillingCycle | null;
|
|
7
|
+
export declare function getPlanFromSearch(search: string): BillingCycle;
|
|
8
|
+
export declare function getPlanFromUrl(): BillingCycle;
|
|
9
|
+
export declare function planToQueryValue(cycle: BillingCycle): 'monthly' | 'annually';
|
|
10
|
+
/**
|
|
11
|
+
* Set ?plan= while preserving other params.
|
|
12
|
+
* Avoid URLSearchParams — it treats bare `+` as a space and would break
|
|
13
|
+
* course_run_key values like HarvardX+CS50+X.
|
|
14
|
+
*/
|
|
15
|
+
export declare function withPlanQuery(search: string, cycle: BillingCycle): string;
|
|
16
|
+
export declare function writePlanQueryParam(cycle: BillingCycle): void;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const PLAN_QUERY_KEY = 'plan';
|
|
2
|
+
/**
|
|
3
|
+
* Map ?plan= values to the internal billing cycle.
|
|
4
|
+
* Accepts monthly, annual, and annually (the public query value).
|
|
5
|
+
*/
|
|
6
|
+
export function parsePlanParam(raw) {
|
|
7
|
+
if (!raw)
|
|
8
|
+
return null;
|
|
9
|
+
const value = raw.trim().toLowerCase();
|
|
10
|
+
if (value === 'monthly')
|
|
11
|
+
return 'monthly';
|
|
12
|
+
if (value === 'annual' || value === 'annually')
|
|
13
|
+
return 'annual';
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
export function getPlanFromSearch(search) {
|
|
17
|
+
var _a, _b;
|
|
18
|
+
const match = /[?&]plan=([^&]*)/i.exec(search);
|
|
19
|
+
if (!match)
|
|
20
|
+
return 'monthly';
|
|
21
|
+
try {
|
|
22
|
+
return (_a = parsePlanParam(decodeURIComponent(match[1]))) !== null && _a !== void 0 ? _a : 'monthly';
|
|
23
|
+
}
|
|
24
|
+
catch (_c) {
|
|
25
|
+
return (_b = parsePlanParam(match[1])) !== null && _b !== void 0 ? _b : 'monthly';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function getPlanFromUrl() {
|
|
29
|
+
if (typeof window === 'undefined')
|
|
30
|
+
return 'monthly';
|
|
31
|
+
return getPlanFromSearch(window.location.search);
|
|
32
|
+
}
|
|
33
|
+
export function planToQueryValue(cycle) {
|
|
34
|
+
return cycle === 'annual' ? 'annually' : 'monthly';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Set ?plan= while preserving other params.
|
|
38
|
+
* Avoid URLSearchParams — it treats bare `+` as a space and would break
|
|
39
|
+
* course_run_key values like HarvardX+CS50+X.
|
|
40
|
+
*/
|
|
41
|
+
export function withPlanQuery(search, cycle) {
|
|
42
|
+
const planValue = planToQueryValue(cycle);
|
|
43
|
+
const raw = search.startsWith('?') ? search.slice(1) : search;
|
|
44
|
+
const parts = raw
|
|
45
|
+
.split('&')
|
|
46
|
+
.filter((part) => (part.length > 0
|
|
47
|
+
&& !part.toLowerCase().startsWith(`${PLAN_QUERY_KEY}=`)));
|
|
48
|
+
parts.push(`${PLAN_QUERY_KEY}=${planValue}`);
|
|
49
|
+
return `?${parts.join('&')}`;
|
|
50
|
+
}
|
|
51
|
+
export function writePlanQueryParam(cycle) {
|
|
52
|
+
if (typeof window === 'undefined')
|
|
53
|
+
return;
|
|
54
|
+
const next = `${window.location.pathname}${withPlanQuery(window.location.search, cycle)}${window.location.hash}`;
|
|
55
|
+
window.history.replaceState(window.history.state, '', next);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=planQuery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planQuery.js","sourceRoot":"","sources":["../../src/utils/planQuery.ts"],"names":[],"mappings":"AAEA,MAAM,cAAc,GAAG,MAAM,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAA8B;IAC3D,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACvC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,UAAU;QAAE,OAAO,QAAQ,CAAC;IAChE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAc;;IAC9C,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,CAAC;QACH,OAAO,MAAA,cAAc,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,mCAAI,SAAS,CAAC;IACnE,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,MAAA,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,mCAAI,SAAS,CAAC;IAC/C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,SAAS,CAAC;IACpD,OAAO,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAmB;IAClD,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,KAAmB;IAC/D,MAAM,SAAS,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC9D,MAAM,KAAK,GAAG,GAAG;SACd,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAChB,IAAI,CAAC,MAAM,GAAG,CAAC;WACZ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,cAAc,GAAG,CAAC,CACxD,CAAC,CAAC;IACL,KAAK,CAAC,IAAI,CAAC,GAAG,cAAc,IAAI,SAAS,EAAE,CAAC,CAAC;IAC7C,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAmB;IACrD,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO;IAC1C,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACjH,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;AAC9D,CAAC","sourcesContent":["import type { BillingCycle } from '../types';\n\nconst PLAN_QUERY_KEY = 'plan';\n\n/**\n * Map ?plan= values to the internal billing cycle.\n * Accepts monthly, annual, and annually (the public query value).\n */\nexport function parsePlanParam(raw: string | null | undefined): BillingCycle | null {\n if (!raw) return null;\n const value = raw.trim().toLowerCase();\n if (value === 'monthly') return 'monthly';\n if (value === 'annual' || value === 'annually') return 'annual';\n return null;\n}\n\nexport function getPlanFromSearch(search: string): BillingCycle {\n const match = /[?&]plan=([^&]*)/i.exec(search);\n if (!match) return 'monthly';\n try {\n return parsePlanParam(decodeURIComponent(match[1])) ?? 'monthly';\n } catch {\n return parsePlanParam(match[1]) ?? 'monthly';\n }\n}\n\nexport function getPlanFromUrl(): BillingCycle {\n if (typeof window === 'undefined') return 'monthly';\n return getPlanFromSearch(window.location.search);\n}\n\nexport function planToQueryValue(cycle: BillingCycle): 'monthly' | 'annually' {\n return cycle === 'annual' ? 'annually' : 'monthly';\n}\n\n/**\n * Set ?plan= while preserving other params.\n * Avoid URLSearchParams — it treats bare `+` as a space and would break\n * course_run_key values like HarvardX+CS50+X.\n */\nexport function withPlanQuery(search: string, cycle: BillingCycle): string {\n const planValue = planToQueryValue(cycle);\n const raw = search.startsWith('?') ? search.slice(1) : search;\n const parts = raw\n .split('&')\n .filter((part) => (\n part.length > 0\n && !part.toLowerCase().startsWith(`${PLAN_QUERY_KEY}=`)\n ));\n parts.push(`${PLAN_QUERY_KEY}=${planValue}`);\n return `?${parts.join('&')}`;\n}\n\nexport function writePlanQueryParam(cycle: BillingCycle): void {\n if (typeof window === 'undefined') return;\n const next = `${window.location.pathname}${withPlanQuery(window.location.search, cycle)}${window.location.hash}`;\n window.history.replaceState(window.history.state, '', next);\n}\n"]}
|
package/dist/utils/provision.js
CHANGED
|
@@ -20,8 +20,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
20
20
|
import { EnvironmentTypes, getSiteConfig } from '@openedx/frontend-base';
|
|
21
21
|
/** Hardcoded edX JWT used only for local/dev checkout against stage Viax. */
|
|
22
22
|
const DEV_SESSION_BODY = {
|
|
23
|
-
payload: 'eyJhbGciOiJSUzUxMiIsImtpZCI6Imxtc3N0YWdlMDAyIiwidHlwIjoiSldUIn0.
|
|
24
|
-
signature: '
|
|
23
|
+
payload: 'eyJhbGciOiJSUzUxMiIsImtpZCI6Imxtc3N0YWdlMDAyIiwidHlwIjoiSldUIn0.eyJhdWQiOiJJbmtvY3VqTGlreXVjc0Vkd2lXYXRkZWJyRWFja21ldkxha0R1aWZLb29zaGtha1dvdyIsImV4cCI6MTc4NzY2OTQ5NiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwiaWF0IjoxNzg3NjY1ODk2LCJpc3MiOiJodHRwczovL2NvdXJzZXMuc3RhZ2UuZWR4Lm9yZy9vYXV0aDIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJUVV8yNjA4XzNZUksiLCJzY29wZXMiOlsidXNlcl9pZCIsImVtYWlsIiwicHJvZmlsZSJdLCJ2ZXJzaW9uIjoiMS4yLjAiLCJzdWIiOiJhZjFiNmY2ZTgxMTk2ZTM5YTg3ZjIzZTNiYzk0Y2M5ZSIsImZpbHRlcnMiOlsidXNlcjptZSJdLCJpc19yZXN0cmljdGVkIjpmYWxzZSwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJ1c2VyX2lkIjo1Mjg1MDg3LCJlbWFpbCI6ImxpcmV0aW05NzBAYm9jYWJseS5jb20iLCJuYW1lIjoidGVzdCB1c2VyIiwiZmFtaWx5X25hbWUiOiIiLCJnaXZlbl9uYW1lIjoiIiwiYWRtaW5pc3RyYXRvciI6ZmFsc2UsInN1cGVydXNlciI6ZmFsc2V9',
|
|
24
|
+
signature: 'FpdQ-RysJxcQxJu-AsKz7HBh_Jh9AzV6TeBkwEHTEW631OnqY6J5HUNOfRR9HlfNqM_X7w4uvpP5TGiTvgqNZm6QWOYmzeNelcntqyLHl-8ZRMQQ3UMp6yDshalxqoNp_mfIbXCzaA0FTB1EXip6R87gG0zqL1DF2keJ5DCf_RvL4MLoNz1z_Hjrd7KDlMAhVCG6G3ylkrSnkFJRMqQloXgIdj2FfOYET7oxboMbv1jkPEOQzmPNvrPARF1WQ9F2lJV3JoBq3CSXYW1b-_6TYSvXafySGAN0vn3RvNFGHsoIBTSIWay_BcbwRpEZRR-nA-v1zW6dCG-euF9mB_6Ysg'
|
|
25
25
|
};
|
|
26
26
|
function parseSessionResponse(data) {
|
|
27
27
|
var _a, _b, _c, _d;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provision.js","sourceRoot":"","sources":["../../src/utils/provision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;;;;;;;;;;AAEH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAWzE,6EAA6E;AAC7E,MAAM,gBAAgB,GAAG;IACvB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"provision.js","sourceRoot":"","sources":["../../src/utils/provision.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * api/provision.ts — exchange the edX session for a viax token.\n *\n * {LMS_BASE_URL}/api/edx-session\n * The LMS reads the edX JWT from browser cookies, exchanges it with viax,\n * and returns a per-user token for authenticated API calls.\n *\n * Dev uses POST with a hardcoded JWT payload (local LMS cookie exchange is\n * unavailable). Stage and prod use GET and rely on the browser session cookies.\n */\n\nimport { EnvironmentTypes, getSiteConfig } from '@openedx/frontend-base';\n\nexport interface EdxSessionResult {\n viaxToken: string, // raw JWT — add 'Bearer ' prefix before use\n customerUid: string,\n partyUid?: string,\n edxUserId?: string,\n username?: string,\n email?: string,\n}\n\n/** Hardcoded edX JWT used only for local/dev checkout against stage Viax. */\nconst DEV_SESSION_BODY = {\n payload: 'eyJhbGciOiJSUzUxMiIsImtpZCI6Imxtc3N0YWdlMDAyIiwidHlwIjoiSldUIn0.eyJhdWQiOiJJbmtvY3VqTGlreXVjc0Vkd2lXYXRkZWJyRWFja21ldkxha0R1aWZLb29zaGtha1dvdyIsImV4cCI6MTc4NzY2OTQ5NiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwiaWF0IjoxNzg3NjY1ODk2LCJpc3MiOiJodHRwczovL2NvdXJzZXMuc3RhZ2UuZWR4Lm9yZy9vYXV0aDIiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJUVV8yNjA4XzNZUksiLCJzY29wZXMiOlsidXNlcl9pZCIsImVtYWlsIiwicHJvZmlsZSJdLCJ2ZXJzaW9uIjoiMS4yLjAiLCJzdWIiOiJhZjFiNmY2ZTgxMTk2ZTM5YTg3ZjIzZTNiYzk0Y2M5ZSIsImZpbHRlcnMiOlsidXNlcjptZSJdLCJpc19yZXN0cmljdGVkIjpmYWxzZSwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJ1c2VyX2lkIjo1Mjg1MDg3LCJlbWFpbCI6ImxpcmV0aW05NzBAYm9jYWJseS5jb20iLCJuYW1lIjoidGVzdCB1c2VyIiwiZmFtaWx5X25hbWUiOiIiLCJnaXZlbl9uYW1lIjoiIiwiYWRtaW5pc3RyYXRvciI6ZmFsc2UsInN1cGVydXNlciI6ZmFsc2V9',\n signature: 'FpdQ-RysJxcQxJu-AsKz7HBh_Jh9AzV6TeBkwEHTEW631OnqY6J5HUNOfRR9HlfNqM_X7w4uvpP5TGiTvgqNZm6QWOYmzeNelcntqyLHl-8ZRMQQ3UMp6yDshalxqoNp_mfIbXCzaA0FTB1EXip6R87gG0zqL1DF2keJ5DCf_RvL4MLoNz1z_Hjrd7KDlMAhVCG6G3ylkrSnkFJRMqQloXgIdj2FfOYET7oxboMbv1jkPEOQzmPNvrPARF1WQ9F2lJV3JoBq3CSXYW1b-_6TYSvXafySGAN0vn3RvNFGHsoIBTSIWay_BcbwRpEZRR-nA-v1zW6dCG-euF9mB_6Ysg'\n};\nfunction parseSessionResponse(data: Record<string, unknown>): EdxSessionResult {\n const viaxToken = (\n data.viaxToken ?? data.token ?? data.access_token ?? data.accessToken\n ) as string | undefined;\n const customerUid = (data.customerUid ?? data.customer_uid) as string | undefined;\n\n if (!viaxToken) throw new Error('Session response missing viax token.');\n if (!customerUid) throw new Error('Session response missing customerUid.');\n\n return {\n viaxToken,\n customerUid,\n partyUid: data.partyUid as string | undefined,\n edxUserId: data.edxUserId as string | undefined,\n username: data.username as string | undefined,\n email: data.email as string | undefined,\n };\n}\n\n/**\n * Fetch the ISO 3166-1 alpha-2 country code from the user's edX account profile.\n * Returns null if the request fails or the field is absent.\n */\nexport async function fetchUserCountry(lmsBaseUrl: string, username: string): Promise<string | null> {\n try {\n const res = await fetch(`${lmsBaseUrl}/api/user/v1/accounts/${encodeURIComponent(username)}`, {\n credentials: 'include',\n });\n if (!res.ok) return null;\n const data = await res.json() as Record<string, unknown>;\n return typeof data.country === 'string' && data.country ? data.country : null;\n } catch {\n return null;\n }\n}\n\nfunction isDevelopment(): boolean {\n return getSiteConfig().environment === EnvironmentTypes.DEVELOPMENT;\n}\n\n/** Exchange the learner's edX session (cookies) for a viax token. */\nexport async function fetchEdxSession(sessionUrl: string): Promise<EdxSessionResult> {\n const headers: Record<string, string> = { Accept: 'application/json' };\n\n let res: Response;\n if (isDevelopment()) {\n res = await fetch(sessionUrl, {\n method: 'POST',\n credentials: 'include',\n headers: {\n ...headers,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(DEV_SESSION_BODY),\n });\n } else {\n res = await fetch(sessionUrl, {\n method: 'POST',\n credentials: 'include',\n headers,\n });\n }\n\n if (!res.ok) {\n throw new Error(\n `Failed to establish checkout session (${res.status}). Please log into edX and try again.`,\n );\n }\n\n const data = await res.json() as Record<string, unknown> & { error?: string };\n if (data.error) throw new Error(String(data.error));\n\n return parseSessionResponse(data);\n}\n"]}
|