@qelos/global-types 4.1.1 → 4.2.1
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/ai-models.ts +301 -0
- package/dist/ai-models.d.ts +32 -0
- package/dist/ai-models.js +279 -0
- package/dist/ai-models.js.map +1 -0
- package/dist/app-configuration-metadata.d.ts +18 -0
- package/dist/app-configuration-metadata.js +3 -0
- package/dist/app-configuration-metadata.js.map +1 -0
- package/dist/auth-configuration-metadata.d.ts +41 -0
- package/dist/auth-configuration-metadata.js +3 -0
- package/dist/auth-configuration-metadata.js.map +1 -0
- package/dist/blueprint.d.ts +74 -0
- package/dist/blueprint.js +33 -0
- package/dist/blueprint.js.map +1 -0
- package/dist/event-subscriber.d.ts +6 -0
- package/dist/event-subscriber.js +3 -0
- package/dist/event-subscriber.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/injectable.d.ts +6 -0
- package/dist/injectable.js +3 -0
- package/dist/injectable.js.map +1 -0
- package/dist/integration-source-status.d.ts +20 -0
- package/dist/integration-source-status.js +21 -0
- package/dist/integration-source-status.js.map +1 -0
- package/dist/integration-sources.d.ts +351 -0
- package/dist/integration-sources.js +141 -0
- package/dist/integration-sources.js.map +1 -0
- package/dist/integration-targets.d.ts +58 -0
- package/dist/integration-targets.js +3 -0
- package/dist/integration-targets.js.map +1 -0
- package/dist/integration.d.ts +39 -0
- package/dist/integration.js +12 -0
- package/dist/integration.js.map +1 -0
- package/dist/mcp-configuration-metadata.d.ts +19 -0
- package/dist/mcp-configuration-metadata.js +14 -0
- package/dist/mcp-configuration-metadata.js.map +1 -0
- package/dist/mcp-endpoint.d.ts +5 -0
- package/dist/mcp-endpoint.js +13 -0
- package/dist/mcp-endpoint.js.map +1 -0
- package/dist/micro-frontend.d.ts +43 -0
- package/dist/micro-frontend.js +3 -0
- package/dist/micro-frontend.js.map +1 -0
- package/dist/navbar-group.d.ts +7 -0
- package/dist/navbar-group.js +3 -0
- package/dist/navbar-group.js.map +1 -0
- package/dist/no-code-components.d.ts +34 -0
- package/dist/no-code-components.js +3 -0
- package/dist/no-code-components.js.map +1 -0
- package/dist/payment-event-resolution.d.ts +31 -0
- package/dist/payment-event-resolution.js +302 -0
- package/dist/payment-event-resolution.js.map +1 -0
- package/dist/payments.d.ts +165 -0
- package/dist/payments.js +3 -0
- package/dist/payments.js.map +1 -0
- package/dist/plugin-crud.d.ts +11 -0
- package/dist/plugin-crud.js +3 -0
- package/dist/plugin-crud.js.map +1 -0
- package/dist/plugin.d.ts +30 -0
- package/dist/plugin.js +3 -0
- package/dist/plugin.js.map +1 -0
- package/dist/qelos-integrator.d.ts +112 -0
- package/dist/qelos-integrator.js +14 -0
- package/dist/qelos-integrator.js.map +1 -0
- package/dist/query-filters.d.ts +7 -0
- package/dist/query-filters.js +3 -0
- package/dist/query-filters.js.map +1 -0
- package/dist/screen-requirement.d.ts +22 -0
- package/dist/screen-requirement.js +3 -0
- package/dist/screen-requirement.js.map +1 -0
- package/dist/workspace-configuration-metadata.d.ts +17 -0
- package/dist/workspace-configuration-metadata.js +3 -0
- package/dist/workspace-configuration-metadata.js.map +1 -0
- package/index.ts +5 -0
- package/integration-source-status.ts +38 -0
- package/integration-sources.ts +11 -0
- package/integration.ts +1 -0
- package/mcp-configuration-metadata.ts +33 -0
- package/mcp-endpoint.ts +12 -0
- package/package.json +11 -5
- package/payment-event-resolution.ts +388 -0
- package/payments.ts +34 -0
- package/tsconfig.json +17 -0
- package/LICENSE +0 -20
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared contracts every Qelos framework integrator (`@qelos/integrator-*`)
|
|
4
|
+
* builds upon. Defined here — rather than in each integrator — so the
|
|
5
|
+
* per-request shape and configuration surface are guaranteed identical across
|
|
6
|
+
* frameworks.
|
|
7
|
+
*
|
|
8
|
+
* `@qelos/sdk` depends on `@qelos/global-types`, so this file deliberately
|
|
9
|
+
* avoids importing from the SDK; instead, {@link QelosContext} is generic over
|
|
10
|
+
* the SDK instance type and integrators specialize it with the concrete
|
|
11
|
+
* `QelosSDK` / `IUser` / `IWorkspace` types from `@qelos/sdk`.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
//# sourceMappingURL=qelos-integrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qelos-integrator.js","sourceRoot":"","sources":["../qelos-integrator.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-filters.js","sourceRoot":"","sources":["../query-filters.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface IScreenRequirement {
|
|
2
|
+
key: string;
|
|
3
|
+
fromHTTP?: {
|
|
4
|
+
uri: string;
|
|
5
|
+
method: string;
|
|
6
|
+
query?: any;
|
|
7
|
+
};
|
|
8
|
+
fromCrud?: {
|
|
9
|
+
name: string;
|
|
10
|
+
identifier?: string;
|
|
11
|
+
query?: any;
|
|
12
|
+
};
|
|
13
|
+
fromBlueprint?: {
|
|
14
|
+
name: string;
|
|
15
|
+
identifier?: string;
|
|
16
|
+
query?: any;
|
|
17
|
+
dependsOn?: string;
|
|
18
|
+
dependsField?: string;
|
|
19
|
+
};
|
|
20
|
+
fromData?: any;
|
|
21
|
+
lazy?: boolean;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screen-requirement.js","sourceRoot":"","sources":["../screen-requirement.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface WorkspaceLabelDefinition {
|
|
2
|
+
title: string;
|
|
3
|
+
description: string;
|
|
4
|
+
value: string[];
|
|
5
|
+
allowedRolesForCreation?: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface WorkspaceConfigurationMetadata {
|
|
8
|
+
isActive: boolean;
|
|
9
|
+
creationPrivilegedRoles: string[];
|
|
10
|
+
viewMembersPrivilegedWsRoles: string[];
|
|
11
|
+
labels: WorkspaceLabelDefinition[];
|
|
12
|
+
allowNonLabeledWorkspaces: boolean;
|
|
13
|
+
allowNonWorkspaceUsers: boolean;
|
|
14
|
+
allowLogo?: boolean;
|
|
15
|
+
createWorkspacePageTitle?: string;
|
|
16
|
+
labelsSelectorTitle?: string;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-configuration-metadata.js","sourceRoot":"","sources":["../workspace-configuration-metadata.ts"],"names":[],"mappings":""}
|
package/index.ts
CHANGED
|
@@ -10,9 +10,14 @@ export * from './query-filters'
|
|
|
10
10
|
export * from './workspace-configuration-metadata'
|
|
11
11
|
export * from './auth-configuration-metadata'
|
|
12
12
|
export * from './app-configuration-metadata'
|
|
13
|
+
export * from './mcp-configuration-metadata'
|
|
14
|
+
export * from './mcp-endpoint'
|
|
15
|
+
export * from './ai-models'
|
|
13
16
|
export * from './integration-sources'
|
|
17
|
+
export * from './integration-source-status'
|
|
14
18
|
export * from './integration'
|
|
15
19
|
export * from './integration-targets'
|
|
16
20
|
export * from './no-code-components';
|
|
17
21
|
export * from './payments';
|
|
22
|
+
export * from './payment-event-resolution';
|
|
18
23
|
export * from './qelos-integrator';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { IntegrationSourceKind } from './integration-sources';
|
|
2
|
+
|
|
3
|
+
export type IntegrationSourceStatusKind = 'connected' | 'failed' | 'unsupported';
|
|
4
|
+
|
|
5
|
+
/** Payload for draft status checks while creating or editing a connection. */
|
|
6
|
+
export interface IIntegrationSourceStatusRequest {
|
|
7
|
+
kind: IntegrationSourceKind;
|
|
8
|
+
metadata: Record<string, unknown>;
|
|
9
|
+
authentication?: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Result of a read-only integration source connection check. */
|
|
13
|
+
export interface IIntegrationSourceStatusResult {
|
|
14
|
+
status: IntegrationSourceStatusKind;
|
|
15
|
+
message: string;
|
|
16
|
+
kind: IntegrationSourceKind;
|
|
17
|
+
checkedAt: string;
|
|
18
|
+
/** Safe provider snippets (no secrets). */
|
|
19
|
+
details?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const PAYMENT_INTEGRATION_SOURCE_KINDS: IntegrationSourceKind[] = [
|
|
23
|
+
IntegrationSourceKind.Sumit,
|
|
24
|
+
IntegrationSourceKind.PayPal,
|
|
25
|
+
IntegrationSourceKind.Paddle,
|
|
26
|
+
IntegrationSourceKind.DodoPayments,
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
/** Integration source kinds with a real, implemented connection status check. */
|
|
30
|
+
export const STATUS_CHECK_SUPPORTED_INTEGRATION_SOURCE_KINDS: IntegrationSourceKind[] = [
|
|
31
|
+
...PAYMENT_INTEGRATION_SOURCE_KINDS,
|
|
32
|
+
IntegrationSourceKind.Http,
|
|
33
|
+
IntegrationSourceKind.OpenAI,
|
|
34
|
+
IntegrationSourceKind.Qelos,
|
|
35
|
+
IntegrationSourceKind.Email,
|
|
36
|
+
IntegrationSourceKind.AWS,
|
|
37
|
+
IntegrationSourceKind.Cloudflare,
|
|
38
|
+
];
|
package/integration-sources.ts
CHANGED
|
@@ -40,6 +40,7 @@ export enum QelosTriggerOperation {
|
|
|
40
40
|
webhook = 'webhook',
|
|
41
41
|
apiWebhook = 'apiWebhook',
|
|
42
42
|
chatCompletion = 'chatCompletion',
|
|
43
|
+
mcpTool = 'mcpTool',
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export enum QelosTargetOperation {
|
|
@@ -88,6 +89,7 @@ export const SumitTargetOperation = {
|
|
|
88
89
|
updatePayment: 'updatePayment',
|
|
89
90
|
// Placeholder for recurring payments
|
|
90
91
|
listRecurringPayments: 'listRecurringPayments',
|
|
92
|
+
beginCheckoutRedirect: 'beginCheckoutRedirect',
|
|
91
93
|
createRecurringPayment: 'createRecurringPayment',
|
|
92
94
|
getRecurringPayment: 'getRecurringPayment',
|
|
93
95
|
updateRecurringPayment: 'updateRecurringPayment',
|
|
@@ -282,6 +284,15 @@ export interface IOpenAISource extends IIntegrationSource {
|
|
|
282
284
|
export interface IClaudeAiSource extends IIntegrationSource {
|
|
283
285
|
_id?: string;
|
|
284
286
|
kind: IntegrationSourceKind.ClaudeAi;
|
|
287
|
+
authentication: {
|
|
288
|
+
token: string;
|
|
289
|
+
};
|
|
290
|
+
metadata: {
|
|
291
|
+
/** Custom API base URL when using a proxy or compatible endpoint. */
|
|
292
|
+
apiUrl?: string;
|
|
293
|
+
defaultModel?: string | null;
|
|
294
|
+
initialMessages?: { role: string; content: string }[];
|
|
295
|
+
};
|
|
285
296
|
}
|
|
286
297
|
|
|
287
298
|
export interface IGeminiSource extends IIntegrationSource {
|
package/integration.ts
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Tool categories that must never be exposed via MCP (e.g. secrets vault). */
|
|
2
|
+
export const MCP_FORBIDDEN_TOOL_IDS = ['secrets'] as const;
|
|
3
|
+
|
|
4
|
+
export interface IMcpExposedTool {
|
|
5
|
+
toolId: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
roles: string[];
|
|
8
|
+
wsRoles: string[];
|
|
9
|
+
wsLabels: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface IMcpConfigurationMetadata {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
permittedCallbackUrls: string[];
|
|
15
|
+
exposedTools: IMcpExposedTool[];
|
|
16
|
+
adminOnly: boolean;
|
|
17
|
+
serverName?: string;
|
|
18
|
+
serverVersion?: string;
|
|
19
|
+
loginUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function sanitizeMcpConfigurationMetadata(
|
|
23
|
+
configuration: IMcpConfigurationMetadata,
|
|
24
|
+
): IMcpConfigurationMetadata {
|
|
25
|
+
const forbidden = new Set<string>(MCP_FORBIDDEN_TOOL_IDS);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
...configuration,
|
|
29
|
+
exposedTools: (configuration.exposedTools || []).filter(
|
|
30
|
+
(tool) => !forbidden.has(tool.toolId),
|
|
31
|
+
),
|
|
32
|
+
};
|
|
33
|
+
}
|
package/mcp-endpoint.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const MCP_ADMIN_PATH = '/api/mcp/admin';
|
|
2
|
+
|
|
3
|
+
export const MCP_DOCS_BASE_URL = 'https://docs.qelos.io/mcp';
|
|
4
|
+
|
|
5
|
+
export const MCP_DOCS_CONFIGURATION_URL = `${MCP_DOCS_BASE_URL}/configuration.html`;
|
|
6
|
+
|
|
7
|
+
export const MCP_DOCS_CLIENTS_URL = `${MCP_DOCS_BASE_URL}/clients.html`;
|
|
8
|
+
|
|
9
|
+
export function buildMcpAdminEndpointUrl(origin: string): string {
|
|
10
|
+
const trimmedOrigin = origin.replace(/\/+$/, '');
|
|
11
|
+
return `${trimmedOrigin}${MCP_ADMIN_PATH}`;
|
|
12
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qelos/global-types",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "index.
|
|
6
|
-
"
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "index.ts",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc"
|
|
10
|
+
},
|
|
7
11
|
"author": "",
|
|
8
12
|
"gitHead": "13468bc51557291935b96b1aeaa837b8b52861e8",
|
|
9
13
|
"license": "MIT",
|
|
10
14
|
"publishConfig": {
|
|
11
15
|
"access": "public"
|
|
12
16
|
},
|
|
13
|
-
"
|
|
14
|
-
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.4.5"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
export type PaymentAdminSuggestion = {
|
|
2
|
+
summary: string;
|
|
3
|
+
action: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type PaymentProviderPublicContext = {
|
|
7
|
+
providerSourceId?: string;
|
|
8
|
+
providerPublicAccountId?: string;
|
|
9
|
+
providerEnvironment?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type PaymentResolutionContext = {
|
|
13
|
+
providerKind?: string;
|
|
14
|
+
operation?: string;
|
|
15
|
+
code?: string;
|
|
16
|
+
status?: number;
|
|
17
|
+
message?: string;
|
|
18
|
+
providerError?: Record<string, unknown> | null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type PaymentEventDocsContext = {
|
|
22
|
+
providerKind?: string;
|
|
23
|
+
eventName?: string;
|
|
24
|
+
operation?: string;
|
|
25
|
+
code?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const PAYMENTS_DOCS_BASE_URL = 'https://docs.qelos.io/payments';
|
|
29
|
+
|
|
30
|
+
const TROUBLESHOOTING_ERROR_CODES = new Set([
|
|
31
|
+
'PAYMENTS_NOT_CONFIGURED',
|
|
32
|
+
'ACTIVE_SUBSCRIPTION_EXISTS',
|
|
33
|
+
'DYNAMIC_AMOUNT_NOT_SET',
|
|
34
|
+
'DYNAMIC_PLAN_REQUIRES_SUBSCRIPTION',
|
|
35
|
+
'DYNAMIC_PLAN_UNSUPPORTED_PROVIDER',
|
|
36
|
+
'MISSING_REDIRECT_URLS',
|
|
37
|
+
'INVALID_CHECKOUT_AMOUNT',
|
|
38
|
+
'INVALID_SUMIT_COMPANY_ID',
|
|
39
|
+
'MISSING_SUMIT_CREDENTIALS',
|
|
40
|
+
'UNSUPPORTED_SUMIT_CURRENCY',
|
|
41
|
+
'INTEGRATION_SOURCE_NOT_FOUND',
|
|
42
|
+
'MISSING_EXTERNAL_PRICE_ID',
|
|
43
|
+
'PLAN_NOT_ACTIVE',
|
|
44
|
+
'SUBSCRIPTION_NOT_PENDING',
|
|
45
|
+
'INVALID_SIGNATURE',
|
|
46
|
+
'WEBHOOK_SECRET_NOT_CONFIGURED',
|
|
47
|
+
'TENANT_NOT_FOUND',
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
function troubleshootingCodeAnchor(code: string) {
|
|
51
|
+
return code.toLowerCase().replace(/_/g, '-');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveProviderEventDocsUrl(providerKind: string, eventName?: string) {
|
|
55
|
+
const provider = providerKind.toLowerCase();
|
|
56
|
+
|
|
57
|
+
if (eventName === 'payment-method-save-failed') {
|
|
58
|
+
return `${PAYMENTS_DOCS_BASE_URL}/events#failed-credit-card-capture-payment-method-save-failed`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (eventName === 'payment-failed' || eventName === 'webhook-processing-failed') {
|
|
62
|
+
return `${PAYMENTS_DOCS_BASE_URL}/events#${provider}-troubleshooting`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (eventName === 'checkout-failed' || eventName === 'provider-call-failed') {
|
|
66
|
+
switch (provider) {
|
|
67
|
+
case 'sumit':
|
|
68
|
+
return `${PAYMENTS_DOCS_BASE_URL}/events#checkout-initiation-failure-checkout-failed-or-provider-call-failed`;
|
|
69
|
+
case 'paddle':
|
|
70
|
+
return `${PAYMENTS_DOCS_BASE_URL}/events#checkout-validation-failure-checkout-failed`;
|
|
71
|
+
case 'paypal':
|
|
72
|
+
return `${PAYMENTS_DOCS_BASE_URL}/events#checkout-and-subscription-setup-failure-checkout-failed-or-provider-call-failed`;
|
|
73
|
+
case 'dodopayments':
|
|
74
|
+
return `${PAYMENTS_DOCS_BASE_URL}/events#checkout-failure-checkout-failed-or-provider-call-failed`;
|
|
75
|
+
default:
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
switch (provider) {
|
|
81
|
+
case 'sumit':
|
|
82
|
+
return `${PAYMENTS_DOCS_BASE_URL}/events#sumit-troubleshooting`;
|
|
83
|
+
case 'paddle':
|
|
84
|
+
return `${PAYMENTS_DOCS_BASE_URL}/events#paddle-troubleshooting`;
|
|
85
|
+
case 'paypal':
|
|
86
|
+
return `${PAYMENTS_DOCS_BASE_URL}/events#paypal-troubleshooting`;
|
|
87
|
+
case 'dodopayments':
|
|
88
|
+
return `${PAYMENTS_DOCS_BASE_URL}/events#dodopayments-troubleshooting`;
|
|
89
|
+
default:
|
|
90
|
+
return `${PAYMENTS_DOCS_BASE_URL}/troubleshooting`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function resolvePaymentEventDocsUrl(context: PaymentEventDocsContext): string {
|
|
95
|
+
const code = context.code?.toUpperCase();
|
|
96
|
+
|
|
97
|
+
if (code && TROUBLESHOOTING_ERROR_CODES.has(code)) {
|
|
98
|
+
return `${PAYMENTS_DOCS_BASE_URL}/troubleshooting#${troubleshootingCodeAnchor(code)}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (context.providerKind) {
|
|
102
|
+
return resolveProviderEventDocsUrl(context.providerKind, context.eventName);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return `${PAYMENTS_DOCS_BASE_URL}/troubleshooting`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function pushUnique(suggestions: PaymentAdminSuggestion[], suggestion: PaymentAdminSuggestion) {
|
|
109
|
+
if (suggestions.some((s) => s.summary === suggestion.summary)) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
suggestions.push(suggestion);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function sumitCredentialsSuggestions(
|
|
116
|
+
suggestions: PaymentAdminSuggestion[],
|
|
117
|
+
message?: string,
|
|
118
|
+
providerError?: Record<string, unknown> | null,
|
|
119
|
+
) {
|
|
120
|
+
const combined = [
|
|
121
|
+
message,
|
|
122
|
+
providerError?.UserErrorMessage,
|
|
123
|
+
providerError?.TechnicalErrorDetails,
|
|
124
|
+
]
|
|
125
|
+
.filter(Boolean)
|
|
126
|
+
.join(' ')
|
|
127
|
+
.toLowerCase();
|
|
128
|
+
|
|
129
|
+
if (
|
|
130
|
+
combined.includes('credential')
|
|
131
|
+
|| combined.includes('apikey')
|
|
132
|
+
|| combined.includes('api key')
|
|
133
|
+
|| combined.includes('companyid')
|
|
134
|
+
|| combined.includes('company id')
|
|
135
|
+
|| combined.includes('unauthorized')
|
|
136
|
+
|| combined.includes('authentication')
|
|
137
|
+
) {
|
|
138
|
+
pushUnique(suggestions, {
|
|
139
|
+
summary: 'Verify Sumit API credentials',
|
|
140
|
+
action: 'Open Admin → Integrations → Sumit, confirm Company ID matches https://app.sumit.co.il/developers/keys/ and re-save a freshly generated API key.',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function sanitizeProviderErrorBody(value: unknown): Record<string, unknown> | null {
|
|
146
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const sanitized: Record<string, unknown> = {};
|
|
151
|
+
for (const [key, nestedValue] of Object.entries(value as Record<string, unknown>)) {
|
|
152
|
+
const normalized = key.replace(/[_-]/g, '').toLowerCase();
|
|
153
|
+
if (
|
|
154
|
+
normalized === 'credentials'
|
|
155
|
+
|| normalized === 'apikey'
|
|
156
|
+
|| normalized === 'companyid'
|
|
157
|
+
|| normalized === 'singleusetoken'
|
|
158
|
+
|| normalized === 'cardnumber'
|
|
159
|
+
|| normalized === 'creditcardnumber'
|
|
160
|
+
|| normalized === 'cvv'
|
|
161
|
+
|| normalized === 'cvc'
|
|
162
|
+
|| normalized === 'password'
|
|
163
|
+
|| normalized === 'secret'
|
|
164
|
+
) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
sanitized[key] = nestedValue;
|
|
168
|
+
}
|
|
169
|
+
return sanitized;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function extractSumitProviderError(responseBody: unknown): Record<string, unknown> | null {
|
|
173
|
+
const sanitized = sanitizeProviderErrorBody(responseBody);
|
|
174
|
+
if (!sanitized) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (sanitized.providerError && typeof sanitized.providerError === 'object') {
|
|
179
|
+
return sanitizeProviderErrorBody(sanitized.providerError);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
sanitized.UserErrorMessage
|
|
184
|
+
|| sanitized.TechnicalErrorDetails
|
|
185
|
+
|| sanitized.Status
|
|
186
|
+
) {
|
|
187
|
+
return sanitized;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (sanitized.Data && typeof sanitized.Data === 'object') {
|
|
191
|
+
return sanitizeProviderErrorBody(sanitized.Data);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return sanitized;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function resolvePaymentProviderPublicContext(
|
|
198
|
+
providerKind: string | undefined,
|
|
199
|
+
metadata?: Record<string, unknown> | null,
|
|
200
|
+
providerSourceId?: string,
|
|
201
|
+
): PaymentProviderPublicContext {
|
|
202
|
+
const context: PaymentProviderPublicContext = {};
|
|
203
|
+
|
|
204
|
+
if (providerSourceId) {
|
|
205
|
+
context.providerSourceId = providerSourceId;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!providerKind || !metadata) {
|
|
209
|
+
return context;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
switch (providerKind) {
|
|
213
|
+
case 'sumit':
|
|
214
|
+
if (metadata.companyId != null && metadata.companyId !== '') {
|
|
215
|
+
context.providerPublicAccountId = String(metadata.companyId);
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
case 'paypal':
|
|
219
|
+
if (typeof metadata.clientId === 'string' && metadata.clientId) {
|
|
220
|
+
context.providerPublicAccountId = metadata.clientId;
|
|
221
|
+
}
|
|
222
|
+
if (typeof metadata.environment === 'string' && metadata.environment) {
|
|
223
|
+
context.providerEnvironment = metadata.environment;
|
|
224
|
+
}
|
|
225
|
+
break;
|
|
226
|
+
case 'paddle':
|
|
227
|
+
if (typeof metadata.environment === 'string' && metadata.environment) {
|
|
228
|
+
context.providerEnvironment = metadata.environment;
|
|
229
|
+
}
|
|
230
|
+
break;
|
|
231
|
+
case 'dodopayments':
|
|
232
|
+
if (typeof metadata.environment === 'string' && metadata.environment) {
|
|
233
|
+
context.providerEnvironment = metadata.environment;
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
default:
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return context;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function appendPaymentProviderContext<T extends Record<string, unknown>>(
|
|
244
|
+
metadata: T,
|
|
245
|
+
providerContext?: PaymentProviderPublicContext,
|
|
246
|
+
): T & Partial<PaymentProviderPublicContext> {
|
|
247
|
+
if (!providerContext) {
|
|
248
|
+
return metadata;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
...metadata,
|
|
253
|
+
...(providerContext.providerSourceId ? { providerSourceId: providerContext.providerSourceId } : {}),
|
|
254
|
+
...(providerContext.providerPublicAccountId ? { providerPublicAccountId: providerContext.providerPublicAccountId } : {}),
|
|
255
|
+
...(providerContext.providerEnvironment ? { providerEnvironment: providerContext.providerEnvironment } : {}),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function buildPaymentAdminSuggestions(context: PaymentResolutionContext): PaymentAdminSuggestion[] {
|
|
260
|
+
const suggestions: PaymentAdminSuggestion[] = [];
|
|
261
|
+
const code = context.code?.toUpperCase();
|
|
262
|
+
const message = context.message?.toLowerCase() || '';
|
|
263
|
+
const providerError = context.providerError;
|
|
264
|
+
|
|
265
|
+
if (code === 'PAYMENTS_NOT_CONFIGURED') {
|
|
266
|
+
pushUnique(suggestions, {
|
|
267
|
+
summary: 'Configure payments for this tenant',
|
|
268
|
+
action: 'Open Admin → Pricing Plans → Configuration, enable payments, and select a payment provider integration source.',
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (code === 'ACTIVE_SUBSCRIPTION_EXISTS') {
|
|
273
|
+
pushUnique(suggestions, {
|
|
274
|
+
summary: 'Cancel the existing subscription before checkout',
|
|
275
|
+
action: 'The billable entity already has an active or trialing subscription. Cancel it with sdk.payments.cancelSubscription(existingSubscriptionId), or pass reset: true on checkout to cancel automatically and start a new subscription.',
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (code === 'DYNAMIC_AMOUNT_NOT_SET') {
|
|
280
|
+
pushUnique(suggestions, {
|
|
281
|
+
summary: 'Set the subscription amount before checkout',
|
|
282
|
+
action: 'This plan is dynamic-priced. An admin must set dynamicAmount on the pending subscription before the user can pay.',
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (code === 'DYNAMIC_PLAN_REQUIRES_SUBSCRIPTION' || code === 'DYNAMIC_PLAN_UNSUPPORTED_PROVIDER') {
|
|
287
|
+
pushUnique(suggestions, {
|
|
288
|
+
summary: 'Use the dynamic-plan checkout flow',
|
|
289
|
+
action: 'Create a pending subscription first, set dynamicAmount (admin), then call checkout with subscriptionId. Dynamic plans require Sumit.',
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (code === 'MISSING_REDIRECT_URLS') {
|
|
294
|
+
pushUnique(suggestions, {
|
|
295
|
+
summary: 'Configure checkout redirect URLs',
|
|
296
|
+
action: 'Add successUrl and cancelUrl in Admin → Pricing Plans → Configuration, or pass them in the checkout request.',
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (code === 'INVALID_CHECKOUT_AMOUNT') {
|
|
301
|
+
pushUnique(suggestions, {
|
|
302
|
+
summary: 'Set a positive checkout amount',
|
|
303
|
+
action: 'For dynamic plans, an admin must set dynamicAmount on the pending subscription before checkout. Static plans need a non-zero monthly/yearly price.',
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (code === 'INVALID_SUMIT_COMPANY_ID' || code === 'MISSING_SUMIT_CREDENTIALS') {
|
|
308
|
+
pushUnique(suggestions, {
|
|
309
|
+
summary: 'Fix Sumit Company ID',
|
|
310
|
+
action: 'Open Admin → Integrations → Sumit and enter the numeric Company ID from https://app.sumit.co.il/developers/keys/.',
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (code === 'UNSUPPORTED_SUMIT_CURRENCY') {
|
|
315
|
+
pushUnique(suggestions, {
|
|
316
|
+
summary: 'Use a Sumit-supported plan currency',
|
|
317
|
+
action: 'Change the plan currency to ILS, USD, or EUR. Sumit does not support other currencies.',
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (code === 'INTEGRATION_SOURCE_NOT_FOUND') {
|
|
322
|
+
pushUnique(suggestions, {
|
|
323
|
+
summary: 'Reconnect the payment provider',
|
|
324
|
+
action: 'The configured paymentSourceId no longer exists. Re-select the Sumit integration in Admin → Pricing Plans → Configuration.',
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (context.providerKind === 'sumit') {
|
|
329
|
+
sumitCredentialsSuggestions(suggestions, context.message, providerError);
|
|
330
|
+
|
|
331
|
+
if (context.operation === 'beginCheckoutRedirect') {
|
|
332
|
+
pushUnique(suggestions, {
|
|
333
|
+
summary: 'Confirm Sumit hosted checkout is enabled',
|
|
334
|
+
action: 'In the Sumit account, verify redirect/hosted checkout is enabled for the merchant and that the API key has billing permissions.',
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (message.includes('unsupported sumit currency')) {
|
|
339
|
+
pushUnique(suggestions, {
|
|
340
|
+
summary: 'Use a Sumit-supported plan currency',
|
|
341
|
+
action: 'Set the plan currency to ILS, USD, or EUR before retrying checkout.',
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (message.includes('missing api key or company id')) {
|
|
346
|
+
pushUnique(suggestions, {
|
|
347
|
+
summary: 'Complete the Sumit integration credentials',
|
|
348
|
+
action: 'Open Admin → Integrations → Sumit and save both Company ID and API key.',
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (
|
|
354
|
+
context.status === 401
|
|
355
|
+
|| context.status === 403
|
|
356
|
+
|| message.includes('401')
|
|
357
|
+
|| message.includes('403')
|
|
358
|
+
) {
|
|
359
|
+
pushUnique(suggestions, {
|
|
360
|
+
summary: 'Payment provider rejected the credentials',
|
|
361
|
+
action: 'Regenerate the provider API key/secret in the provider dashboard and update the integration source in Admin → Integrations.',
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (
|
|
366
|
+
code === 'INTEGRATION_TARGET_FAILED'
|
|
367
|
+
&& suggestions.length === 0
|
|
368
|
+
&& context.providerKind === 'sumit'
|
|
369
|
+
) {
|
|
370
|
+
pushUnique(suggestions, {
|
|
371
|
+
summary: 'Review Sumit account configuration',
|
|
372
|
+
action: 'Verify Company ID and API key in Admin → Integrations → Sumit, then retry checkout. If it still fails, check Sumit developer logs for the rejected beginredirect request.',
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return suggestions;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export function buildPaymentEventDescription(
|
|
380
|
+
baseDescription: string,
|
|
381
|
+
providerError?: Record<string, unknown> | null,
|
|
382
|
+
): string {
|
|
383
|
+
const userMessage = providerError?.UserErrorMessage;
|
|
384
|
+
if (typeof userMessage === 'string' && userMessage.trim()) {
|
|
385
|
+
return `${baseDescription}: ${userMessage.trim()}`;
|
|
386
|
+
}
|
|
387
|
+
return baseDescription;
|
|
388
|
+
}
|