@edx/frontend-app-subscription-checkout 1.22.2 → 1.22.3
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.
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The backend allows one open subscription cart per learner. A refused
|
|
5
5
|
* create comes back as the generic "Validation check is failed" message, with
|
|
6
|
-
* the reason as `<CODE>:<biId>`
|
|
6
|
+
* the reason as `<CODE>:<biId>` in `extensions` — on the error entry or at the
|
|
7
|
+
* top of the response (`extensions.validation.errors`, which is where it arrives today):
|
|
7
8
|
* OPEN_CART_EXISTS:<cart biId> — an earlier cart is still open
|
|
8
9
|
* ACTIVE_SUBSCRIPTION:<subscription biId> — the learner already subscribes
|
|
9
10
|
* The code is searched for anywhere in the error, so a change in where the
|
|
@@ -13,14 +14,15 @@ import { GqlError } from './graphqlClient';
|
|
|
13
14
|
const REFUSAL_PATTERN = /(OPEN_CART_EXISTS|ACTIVE_SUBSCRIPTION):([A-Za-z0-9_-]+)/;
|
|
14
15
|
/** The refusal behind a failed createCart, or null when the error is something else. */
|
|
15
16
|
export function parseCartRefusal(err) {
|
|
17
|
+
var _a;
|
|
16
18
|
if (!(err instanceof Error))
|
|
17
19
|
return null;
|
|
18
20
|
let text = err.message;
|
|
19
21
|
if (err instanceof GqlError) {
|
|
20
22
|
try {
|
|
21
|
-
text += ` ${JSON.stringify(err.errors)}`;
|
|
23
|
+
text += ` ${JSON.stringify(err.errors)} ${JSON.stringify((_a = err.extensions) !== null && _a !== void 0 ? _a : null)}`;
|
|
22
24
|
}
|
|
23
|
-
catch (
|
|
25
|
+
catch (_b) {
|
|
24
26
|
// Unserialisable extensions — the message alone is all there is to search.
|
|
25
27
|
}
|
|
26
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cartRefusal.js","sourceRoot":"","sources":["../../src/utils/cartRefusal.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cartRefusal.js","sourceRoot":"","sources":["../../src/utils/cartRefusal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAS3C,MAAM,eAAe,GAAG,yDAAyD,CAAC;AAElF,wFAAwF;AACxF,MAAM,UAAU,gBAAgB,CAAC,GAAY;;IAC3C,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC;IACvB,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,IAAI,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAA,GAAG,CAAC,UAAU,mCAAI,IAAI,CAAC,EAAE,CAAC;QACrF,CAAC;QAAC,WAAM,CAAC;YACP,2EAA2E;QAC7E,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAoB,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAsB;IACxD,SAAS;IACT,kBAAkB;IAClB,iBAAiB;IACjB,gBAAgB;IAChB,mBAAmB;IACnB,gBAAgB;IAChB,mBAAmB;CACpB,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG,gFAAgF,CAAC;AAC3H,MAAM,CAAC,MAAM,iCAAiC,GAAG,wFAAwF,CAAC;AAC1I,MAAM,CAAC,MAAM,4BAA4B,GAAG,mFAAmF,CAAC","sourcesContent":["/**\n * utils/cartRefusal.ts — read why createTwouCart was refused (SUBS-1127).\n *\n * The backend allows one open subscription cart per learner. A refused\n * create comes back as the generic \"Validation check is failed\" message, with\n * the reason as `<CODE>:<biId>` in `extensions` — on the error entry or at the\n * top of the response (`extensions.validation.errors`, which is where it arrives today):\n * OPEN_CART_EXISTS:<cart biId> — an earlier cart is still open\n * ACTIVE_SUBSCRIPTION:<subscription biId> — the learner already subscribes\n * The code is searched for anywhere in the error, so a change in where the\n * platform nests it does not break the lookup.\n */\nimport { GqlError } from './graphqlClient';\n\nexport type CartRefusalCode = 'OPEN_CART_EXISTS' | 'ACTIVE_SUBSCRIPTION';\n\nexport interface CartRefusal {\n code: CartRefusalCode,\n biId: string,\n}\n\nconst REFUSAL_PATTERN = /(OPEN_CART_EXISTS|ACTIVE_SUBSCRIPTION):([A-Za-z0-9_-]+)/;\n\n/** The refusal behind a failed createCart, or null when the error is something else. */\nexport function parseCartRefusal(err: unknown): CartRefusal | null {\n if (!(err instanceof Error)) return null;\n let text = err.message;\n if (err instanceof GqlError) {\n try {\n text += ` ${JSON.stringify(err.errors)} ${JSON.stringify(err.extensions ?? null)}`;\n } catch {\n // Unserialisable extensions — the message alone is all there is to search.\n }\n }\n const match = REFUSAL_PATTERN.exec(text);\n return match ? { code: match[1] as CartRefusalCode, biId: match[2] } : null;\n}\n\n/**\n * Open-cart states whose payment is still being worked on. Cancelling one of\n * these could orphan a subscription that is half-made or already approved, so\n * the learner waits instead; the stale-cart job abandons it if it never finishes.\n */\nexport const CART_IN_PROGRESS_STATES: readonly string[] = [\n 'CONFIRM',\n 'SANCTION_STARTED',\n 'SANCTION_PASSED',\n 'STRIPE_STARTED',\n 'STRIPE_SUBSCRIBED',\n 'PAYPAL_STARTED',\n 'PAYPAL_SUBSCRIBED',\n];\n\nexport const ALREADY_SUBSCRIBED_MESSAGE = 'You already have an active subscription. Go to Manage subscription to view it.';\nexport const PREVIOUS_ORDER_PROCESSING_MESSAGE = 'Your previous order is still being processed. Please wait a few minutes and try again.';\nexport const PREVIOUS_ORDER_STUCK_MESSAGE = 'We could not clear your previous checkout. Please refresh the page and try again.';\n"]}
|
|
@@ -14,12 +14,15 @@ export interface GqlErrorEntry {
|
|
|
14
14
|
extensions?: unknown;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* A GraphQL error response. Keeps the full `errors` list
|
|
18
|
-
* comes back as a generic message, with its
|
|
17
|
+
* A GraphQL error response. Keeps the full `errors` list and the response-level
|
|
18
|
+
* `extensions`: a refused validation comes back as a generic message, with its
|
|
19
|
+
* reason in `extensions` — either on the error entry or, as createTwouCart does,
|
|
20
|
+
* at the top of the response (`extensions.validation.errors`).
|
|
19
21
|
*/
|
|
20
22
|
export declare class GqlError extends Error {
|
|
21
23
|
readonly errors: GqlErrorEntry[];
|
|
22
|
-
|
|
24
|
+
readonly extensions: unknown;
|
|
25
|
+
constructor(message: string, errors: GqlErrorEntry[], extensions?: unknown);
|
|
23
26
|
}
|
|
24
27
|
export declare function createGqlClient({ apiUrl }: {
|
|
25
28
|
apiUrl: string;
|
|
@@ -8,14 +8,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
11
|
-
* A GraphQL error response. Keeps the full `errors` list
|
|
12
|
-
* comes back as a generic message, with its
|
|
11
|
+
* A GraphQL error response. Keeps the full `errors` list and the response-level
|
|
12
|
+
* `extensions`: a refused validation comes back as a generic message, with its
|
|
13
|
+
* reason in `extensions` — either on the error entry or, as createTwouCart does,
|
|
14
|
+
* at the top of the response (`extensions.validation.errors`).
|
|
13
15
|
*/
|
|
14
16
|
export class GqlError extends Error {
|
|
15
|
-
constructor(message, errors) {
|
|
17
|
+
constructor(message, errors, extensions) {
|
|
16
18
|
super(message);
|
|
17
19
|
this.name = 'GqlError';
|
|
18
20
|
this.errors = errors;
|
|
21
|
+
this.extensions = extensions;
|
|
19
22
|
}
|
|
20
23
|
}
|
|
21
24
|
export function createGqlClient({ apiUrl }) {
|
|
@@ -41,7 +44,7 @@ export function createGqlClient({ apiUrl }) {
|
|
|
41
44
|
const json = yield res.json();
|
|
42
45
|
if ((_a = json.errors) === null || _a === void 0 ? void 0 : _a.length) {
|
|
43
46
|
const msg = ((_b = json.errors[0]) === null || _b === void 0 ? void 0 : _b.message) || 'GraphQL error';
|
|
44
|
-
throw new GqlError(msg, json.errors);
|
|
47
|
+
throw new GqlError(msg, json.errors, json.extensions);
|
|
45
48
|
}
|
|
46
49
|
return json.data;
|
|
47
50
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphqlClient.js","sourceRoot":"","sources":["../../src/utils/graphqlClient.ts"],"names":[],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"graphqlClient.js","sourceRoot":"","sources":["../../src/utils/graphqlClient.ts"],"names":[],"mappings":";;;;;;;;;AAyBA;;;;;GAKG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IAKjC,YAAY,OAAe,EAAE,MAAuB,EAAE,UAAoB;QACxE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAC7B,EAAE,MAAM,EAAsB;IAE9B,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAEpE,IAAI,KAAK,GAAkB,IAAI,CAAC;IAEhC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAQ,EAAE;QACrC,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,GAAG,EAAE,CAAC;IAC5D,CAAC,CAAC;IAEF,MAAM,GAAG,GAAc,kCAAwC,EAAE,uEAAnC,KAAK,EAAE,SAAS,EAAE,KAAK,GAAG,KAAK;;QAC3D,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,+DAA+D,CAAC,CAAC;QAE7F,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;YAC9B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,EAAE;YACrE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;SAC3C,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACjC,KAAK,GAAG,IAAI,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,IAAI,GAAgB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,MAAA,IAAI,CAAC,MAAM,0CAAE,MAAM,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,CAAA,MAAA,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,0CAAE,OAAO,KAAI,eAAe,CAAC;YACvD,MAAM,IAAI,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC,CAAA,CAAC;IAEF,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * api/graphqlClient.ts — authed GraphQL client.\n *\n * No token is required at construction. Call setToken(viaxToken) after\n * GET /edx-session returns — typically on checkout page mount.\n *\n * On 401 the token is cleared and the request fails — the mutation will\n * surface the error and the user can retry.\n */\nimport type { GqlRunner } from '../types';\n\nexport interface GqlErrorEntry {\n message?: string,\n /** Server detail behind the message, e.g. a validator's refusal code (SUBS-1127). */\n extensions?: unknown,\n}\n\ninterface GqlResponse {\n\n data?: any,\n errors?: GqlErrorEntry[],\n /** Response-level detail; the createTwouCart validator puts its refusal code here. */\n extensions?: unknown,\n}\n\n/**\n * A GraphQL error response. Keeps the full `errors` list and the response-level\n * `extensions`: a refused validation comes back as a generic message, with its\n * reason in `extensions` — either on the error entry or, as createTwouCart does,\n * at the top of the response (`extensions.validation.errors`).\n */\nexport class GqlError extends Error {\n readonly errors: GqlErrorEntry[];\n\n readonly extensions: unknown;\n\n constructor(message: string, errors: GqlErrorEntry[], extensions?: unknown) {\n super(message);\n this.name = 'GqlError';\n this.errors = errors;\n this.extensions = extensions;\n }\n}\n\nexport function createGqlClient(\n { apiUrl }: { apiUrl: string },\n): { gql: GqlRunner, setToken: (token: string) => void } {\n if (!apiUrl) throw new Error('createGqlClient: apiUrl is required');\n\n let token: string | null = null;\n\n const setToken = (raw: string): void => {\n token = raw.startsWith('Bearer ') ? raw : `Bearer ${raw}`;\n };\n\n const gql: GqlRunner = async (query, variables, retry = false) => {\n if (!token) throw new Error('No viax token available. Please try placing your order again.');\n\n const res = await fetch(apiUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: token },\n body: JSON.stringify({ query, variables }),\n });\n\n if (res.status === 401 && !retry) {\n token = null;\n throw new Error('Session expired. Please try placing your order again.');\n }\n\n const json: GqlResponse = await res.json();\n if (json.errors?.length) {\n const msg = json.errors[0]?.message || 'GraphQL error';\n throw new GqlError(msg, json.errors, json.extensions);\n }\n return json.data;\n };\n\n return { gql, setToken };\n}\n"]}
|